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

Add gridded layout optimizer #976

Merged
merged 15 commits into from
Sep 11, 2024
Merged

Conversation

misi9170
Copy link
Collaborator

@misi9170 misi9170 commented Sep 5, 2024

This pull requests adds a new "optimization" method that simply searches for the gridded layout (either a square grid or a hexagonal grid) that fits the most wind turbines into a specified boundary area. The spacing in the grid is fixed by the user, but the grid's rotation and translation is optimized (via a simple parameter sweep) to fit the most turbines into the boundary.

FlorisModel.run() is not called, so there is no wake or AEP calculation---the optimizer simply chooses the grid that fits the most turbines. Where multiple rotations/translations result in the same number of turbines, the result returned is (arbitrarily) the first of those. @ejsimley has suggested we could consider a "tie-breaker" of running an AEP calculation for all layouts that have the maximum number of turbines and selecting that with the highest AEP; for now, this has been left as future work.

There is no requirement that the boundary be a single area---users may specify a boundary with separate areas without issue.

The core code is complete. However, it currently runs somewhat slower than I'd hoped, and I intend to profile the code to see if there are any easy ways to speed it up.

To do:

  • Core code implemented
  • Existing tests pass
  • New tests added
  • Speed profiled
  • Low-hanging fruit for speed-ups exploited
  • Example demonstrating capability
  • Documentation

@misi9170
Copy link
Collaborator Author

misi9170 commented Sep 10, 2024

After profiling, not surprisingly it appears that the slowest part of the code is that which checks, point by point, whether candidate locations are inside the boundary. This code relies on shapely's contains method, which is called inside a nested loop: the outer loop is over the candidate rotations/translations, and the inner loop (represented as a list comprehension) is over the points within the candidate layout.

I tried a couple of quick things to speed this up, both of which failed:

  1. Using numba's @jit decorator on the test_point_in_bounds method, which is simply a wrapper for shapely.contains() (numba could not recognize the shapely objects).
  2. Running shapely.contains() on a MultiPoint object, rather than looping over each Point, to try to remove one level of the nested loop (shapely.contains() appears to check whether all of the points are contained in the shape and returns a scalar boolean value, rather than checking whether each point is contained as we need).

Overall, I think we can treat the code as fast enough for now. When I run the new example 004_generate_gridded_layout.py with min_dist_D=0.5, the 1000m x 1000m square boundary can fit 256 turbines, and the code spends about 1s in the test_point_in_bounds function running on my laptop.

@misi9170
Copy link
Collaborator Author

@paulf81 this is now ready for review

@paulf81 paulf81 self-requested a review September 10, 2024 20:05
# Create the default grid

# use min_dist, hexagonal packing, and boundaries to create a grid.
d = 1.1 * np.sqrt((self.xmax**2 - self.xmin**2) + (self.ymax**2 - self.ymin**2))
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just curious why 1.1?

Copy link
Collaborator Author

@misi9170 misi9170 Sep 11, 2024

Choose a reason for hiding this comment

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

Just as a little bit of over engineering to ensure that d is sufficient to cover the boundary area, even after translations/rotations. May not be necessary, but I thought just easy to add a safety factor.

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.

Overall this looks really great! All tests pass and examples run and produce clear results. Doc is also very clear. Approving and leave remaining minor comments up to you @misi9170

@paulf81 paulf81 merged commit 56921d8 into NREL:develop Sep 11, 2024
8 checks passed
@misi9170 misi9170 mentioned this pull request Sep 27, 2024
@misi9170 misi9170 mentioned this pull request Oct 7, 2024
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants