Skip to content

Conversation

@JasperShell
Copy link
Contributor

TurbOPark implemented like Gauss wake model using sequential_solver

The current implementation of TurbOPark follows Orsted's original implementation (https://github.com/OrstedRD/TurbOPark), and has a dedicated solver, turbopark_solver in solver.py.
However, the current implementation does not match the results of Orsted's model.

This pull request proposes a new implementation of the TurbOPark wake model, turboparkgauss.py. Instead of introducing a dedicated solver, it makes use of the sequential_solver, the same as used for Gauss (GCH) wake model.
This new implementation gives exact match with the original Orsted model.
It also improves the modularity of Floris, because this implementation doesn't need a dedicated solver.

Related issue

The new implementation benefits from the cubature_grid integration scheme that was introduced in #649. It uses:

solver:
  type: turbine_cubature_grid
  turbine_grid_points: 4    # any value greater or equal to 3 should work

Test cases

All test cases are stored in the PR_TurbOParkGauss folder.
In these examples, all turbines have a constant CT of 0.75. Wind speed is fixed to 8.0m/s and TI is set to 6%.

Example 1: Single wake

This example is based on Case_SingleTurbineWake.ipynb and Case_SingleTurbineWake.yaml
NOTE: This test case is built in Floris v3.6.
Because the new implementation uses the sequential_solver, also the full_flow_sequential_solver can be used to visualize a single wake. This is not possible with the current TurbOPark implementation.
The new implementation is compared with Orsted's model and with my own ShellWakes tool.
Screenshot 2024-05-14 102759

Example 2: Wind direction sweep

This example is based on Case_TwinPark_TurbOPark_implementation.py, Case_TwinPark_TurbOPark.yaml and Case_TwinPark_TurbOParkGauss.yaml.
NOTE: This test case is built in Floris v4.0.
A wind direction sweep is done with 2 wind turbines.
Example2

Example 3: Row of turbines

This example is based on Case_Rowpark_TurbOPark.py, Case_RowPark_TurbOPark.yaml and Case_RowPark_TurbOParkGauss.yaml
NOTE: This test case is built in Floris v4.0.
A fully aligned wind farm of 10 wind turbines with 5D spacing.
Example3

Examples 2 and 3 show the mismatch of the original implementation of turbopark.py and the matching implementation of turboparkgauss.py.

Note that in the same way, other wake models can be implemented that use the sequential_solver, as long as the wake profiles are smooth. Models like, gaussian, super-gaussian, double-gaussian, polynomial (Larson), and even EV.
Only hat-shaped models, like Jensen, or models with integrated super-positioning, like CC, require a dedicated solver.

@rafmudaf rafmudaf added the enhancement An improvement of an existing feature label May 14, 2024
@misi9170 misi9170 self-requested a review May 23, 2024 15:55
@rafmudaf rafmudaf changed the title Feature/turbopark as gauss Implement TurbOPark as a Gaussian model May 24, 2024
@misi9170
Copy link
Collaborator

misi9170 commented Jun 4, 2024

Hi @JasperShell , thank you again for taking the time to dig into FLORIS and for submitting this pull request. I've now had more of a chance to start working with it, and I've started with reorganizing the PR_TurbOParkGauss folder and turning it into an example in the examples/ directory (under a new subdirectory, examples_turbopark).

The key changes I've made are:

  • Condensing the floris input files into single files rather than using !includes throughout (not that there was anything wrong there, but just to be more consistent with other examples in the repository)
  • Condensing the two case scripts and one case notebook into a single script (001_compare_turbopark_implementations.py)
  • Renaming some variables and adding comments to the scripts; cleaning up the plots somewhat
  • Constructing the constant C_T model directly in the script
  • Moving the files over to the examples/examples_turbopark directory

Now, running 001_compare_turbopark_implementations.py produces the following figures (which match those from the case scripts/notebooks):
turbopark_1
turbopark_2

@misi9170
Copy link
Collaborator

misi9170 commented Jun 4, 2024

The process I'm planning for working on this PR is

  1. Reorganize examples
  2. Write regression tests (likely based on examples)
  3. Review models and model implementations, possibly request/discuss changes
  4. Add/update documentation
  5. Check tests still pass, then ask @rafmudaf / others to review

@misi9170
Copy link
Collaborator

misi9170 commented Jul 2, 2024

@paulf81 @bayc @rafmudaf this is now ready for review. As I've already been through it thoroughly, I'm happy to merge once one of you is happy. @Bartdoekemeijer , I've also added you as a reviewer in case you'd like to take a look and provide any comments.

@misi9170
Copy link
Collaborator

misi9170 commented Jul 2, 2024

I'll document here for future reference that this implementation has optional mirror wakes to model the effect of the ground or sea surface. The Boolean field include_mirror_wake in the wake_velocity_parameters>turboparkgauss dictionary on the floris input yaml can be used to switch this on and off (defaults to True, i.e., include mirror wakes).

Mirror wake functionality is also available in the empirical Gaussian (EmG) model; but there, it is currently hardcoded to True. I will create an issue to move this option up to the user level on the FLORIS yaml, in the same way as this PR does for the Turboparkgauss model.

more faithful to the original description provided by Nygaard et al and uses
the sequential_solver, and compares it to the existing implementation in
Floris.
"""
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we add here a note on where the data comparison_data comes from, is it from one of the referenced papers? Or the website?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The testcases were my own, so not based on literature. The comparison data comes from running Orsted's Matlab code.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I've now added a text comment saying that the comparison data comes from running Ørsted's Matlab code.


# Plot the data and compare
ax[1].scatter(
turbines, df_rowpark["wws"], s=80, marker="o", c="k", label="Orsted - TurbOPark"
Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe if not too bulky could add (Source: ...)

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.

Thank you very much @JasperShell for this contribution! Only very minor comments from me so I'm going to approve. But I did have two optional thoughts I wanted to check with you and also @misi9170:

  1. Should we add a warning to the former turbopark model if we have a preference toward the new?
  2. More for @misi9170 , the tests are failing on my computer locally but that is due to randomoptregtest so I'll open a separate issue

@JasperShell
Copy link
Contributor Author

@paulf81, thanks for approving this pull request.

On your first point, first of all it is primarily up to you and @misi9170 as owners of the tool.
I would add a warning that the original implementation deviates from Orsted's reference code, and that therefore a new implementation is introduced. I would also add that at some point the original implementation will be phased out.
A similar message is proposed for the documentation, about which @misi9170 and I connected over email.

@paulf81
Copy link
Collaborator

paulf81 commented Jul 5, 2024

Thank you for the comments @JasperShell , I'll meet up with @misi9170 and I think we can get this merged very soon!

@misi9170 misi9170 merged commit 296628a into NatLabRockies:develop Jul 18, 2024
@misi9170 misi9170 mentioned this pull request Oct 7, 2024
4 tasks
@mr483
Copy link

mr483 commented Jan 14, 2026

Thank you all for working on this! I'm (very belatedly) trying to integrate this into my local version of FLORIS v3.1 for legacy reasons, where we have some other connected applications and have not yet updated. I'm running into some issues downstream. I ran both the test case included in this PR as well as the Orsted test case (https://github.com/OrstedRD/TurbOPark/blob/main/TurbOParkExamples.pdf) and in both cases, I get slightly more wake recovery downstream:

image image

My thought is that this is coming from a change affecting SOSFS or some other aspect of wake combination, since the first turbine matches very well. I'm going to compare against the latest version, but just thought I'd write here in case something immediately springs to mind as to what could be happening here.

@misi9170
Copy link
Collaborator

Hi @mr483 ,

My first guess is that this has to do with using a square grid for points, rather than the cubature grid suggested for the turboparkguass model. The cubature grid type was introduced in #649 , and released in FLORIS v3.4.

I'm not 100% sure about this, but I do think this could lead to the differences in results that you are seeing (or at least contribute to them).

@mr483
Copy link

mr483 commented Jan 14, 2026

That was my thought too, except that even when I increase the grid resolution to very high numbers (100 for example), the magnitude of the error is unchanged. Anyway, I will keep investigating, thanks for the tip.

EDIT: Ah - I see why cubature would normally be expected to give different results, since radius_ratio is 0.5 on the standard rectangular grid. But for me, I changed this a while ago to be 1.0 and then do postprocessing in rotor-averaging function to include only points within the rotor area, so this should be unaffected.

@mr483
Copy link

mr483 commented Jan 15, 2026

In the unlikely case someone else needs to implement this in an older FLORIS version (<4.1), the relevant thing which I was missing was the bugfix for the None turbulence model: #894

After that is implemented, matches perfectly.

@misi9170
Copy link
Collaborator

@mr483 , good sleuthing, and thanks for posting the solution here!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement An improvement of an existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants