Skip to content
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

Allow yaw optimization with disabled turbines #1027

Merged
merged 11 commits into from
Nov 18, 2024

Conversation

misi9170
Copy link
Collaborator

@misi9170 misi9170 commented Nov 15, 2024

In #1016, @MiguelMarante raised the issue that the Serial Refine yaw optimizer ignores that some turbines are disabled. I converted this to an issue in #1022 .

This pull request enables that behavior, both for the Serial Refine routine as well as the Geometric yaw optimizer. Note that yaw optimization using Scipy is still not supported for the "mixed-operation" model (I looked into enabling this as well, but this will take more time and is not the priority).

Affected areas of the code

In making the necessary changes to the yaw optimization routines, I also had to make the following changes:

  • floris/floris_model.py: FlorisModel.set() was incorrectly treating lower power setpoints as though they were the default, which caused them to be ignored and reset when set() is called again. This was introduced in Move FlorisInterface .reinitialize() / .calculate_wake() to .set() / .run() #823 by an erroneous commit of mine, a00bff8. This bug meant that calling set(disabled_turbines=[...]) followed by any other set() call would drop the information about disabling turbines.
  • floris/core/turbine/operation_models.py: Clean up of the MixedOperationModel to work better with yaw optimization.

I've also added a new example, examples/examples_control_optimization/008_....py, which is based heavily on @MiguelMarante 's example script in #1016.

Notes

In the yaw optimization routines, the solution involves passing power_setpoints through from the users fmodel to the fmodel_subset attributes on the yaw optimization routines. While this works fine, it is yet another example of where control setpoints have to be piped through various pieces of the FLORIS code. To enable other setpoints to be used while optimizing yaw, this process will have to be repeated. We could consider moving to some sort of high-level data structure (possible just a dictionary) for the control setpoints, so that they can be lumped together and passed through the FLORIS code more readily.

Results

Running the new example produces (removing the progress output from Serial Refine):

Serial Refine optimized yaw angles (all turbines active) [deg]:
 0    [25.0, 22.65625, 0.0]
1    [25.0, 22.65625, 0.0]
Name: yaw_angles_opt, dtype: object

Geometric optimized yaw angles (all turbines active) [deg]:
 0    [19.9952335557674, 19.9952335557674, 0.0]
1    [19.9952335557674, 19.9952335557674, 0.0]
Name: yaw_angles_opt, dtype: object

Serial Refine optimized yaw angles (some turbines disabled) [deg]:
 0    [20.3125, 0.0, 0.0]
1      [0.0, 25.0, 0.0]
Name: yaw_angles_opt, dtype: object

Geometric optimized yaw angles (some turbines disabled) [deg]:
 0    [14.990467111534794, 0.0, 0.0]
1      [0.0, 19.9952335557674, 0.0]

which is in line with expectations, see #1022.

@misi9170 misi9170 added bug Something isn't working enhancement An improvement of an existing feature labels Nov 15, 2024
@misi9170 misi9170 mentioned this pull request Nov 15, 2024
4 tasks
Copy link
Collaborator

@paulf81 paulf81 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all looks good @misi9170 , I left a few small comments. Going to approve but one additional thought I had is we could perhaps add a small test or two to ensure we keep this desired behavior, but isn't clearly necessary

@misi9170
Copy link
Collaborator Author

This all looks good @misi9170 , I left a few small comments. Going to approve but one additional thought I had is we could perhaps add a small test or two to ensure we keep this desired behavior, but isn't clearly necessary

Thanks @paulf81 --- testing is a good idea, I'll write a small test to check it. I don't see any comments though---perhaps those got lost somehow?

@paulf81
Copy link
Collaborator

paulf81 commented Nov 18, 2024

This all looks good @misi9170 , I left a few small comments. Going to approve but one additional thought I had is we could perhaps add a small test or two to ensure we keep this desired behavior, but isn't clearly necessary

Thanks @paulf81 --- testing is a good idea, I'll write a small test to check it. I don't see any comments though---perhaps those got lost somehow?

Huh! They did all disappear, bummer! I don't think they were particularly insightful, from memory they were like:

  1. Is the difference between floris.copy() versus copy.deepcopy() important?
  2. Just flagging we may need to one day route the helix setpoints through the same architecture (but not this PR)
  3. Checking my understanding of the example outputs that the yaw angles of disabled turbines are meaningless and can be anything

@misi9170
Copy link
Collaborator Author

misi9170 commented Nov 18, 2024

@paulf81 on those points:

  1. The deepcopy() is needed to copy over the power_setpoints. Currently, FlorisModel.copy() doesn't copy the control setpoints (yaw_angles, power_setpoints, etc) because it creates a FLORIS dictionary, which doesn't contain those values.
  2. Agreed. This is what I was trying to get at in the Notes section of the PR description---I think we need a better solution for passing around the control setpoints generally. Depending on what this looks like, it may also solve the copy() problem above.
  3. Yes, that's right. I toyed around with having special handling that would automatically set the yaw angles to zero for disabled turbines in the output dictionary. This happens somewhat naturally in the YawOptimizerGeometric code, because yaw angles are initialized at zero, but it was not immediately obvious how this should be done in YawOptimizerSR and besides, it's not really true that the "optimal" yaw angle is zero for a disabled turbine anyway. Still, this may confuse users, and perhaps having special handling to set those yaw angles to zero would be good (alternatively, set them to NaN, which could be done in YawOptimizerGeometric too).

I've now added tests for:

  1. setting simultaneously vs in multiple calls when using the "mixed" operation model
  2. Basic yaw optimization behavior for Serial Refine
  3. Yaw optimization behavior for Serial Refine when turbines are disabled
  4. The above two for the geometric yaw optimizer also

@paulf81
Copy link
Collaborator

paulf81 commented Nov 18, 2024

Thanks @misi9170 good to go on my end!

@misi9170
Copy link
Collaborator Author

@paulf81 I have now added a small piece of code in the constructor of the base YawOptimization class that automatically assigns minimum and maximum yaw angles of zero to disabled turbines. This does help make the output yaw angles more intuitive---thanks for the suggestion.

@misi9170 misi9170 merged commit 64f9dfa into NREL:main Nov 18, 2024
8 checks passed
@misi9170 misi9170 deleted the bugfix/mixed-op-yaw-opt branch November 18, 2024 23:14
misi9170 added a commit that referenced this pull request Nov 18, 2024
@misi9170 misi9170 restored the bugfix/mixed-op-yaw-opt branch November 18, 2024 23:22
@misi9170
Copy link
Collaborator Author

I incorrectly merged this PR directly into the main branch. I have forced pushed the reversion back to the previous commit on main, so main no longer contains this PR's changes. I've opened a new PR #1031 to merge this code into develop, as intended.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working enhancement An improvement of an existing feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants