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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions docs/layout_optimization.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,27 @@ shading indicating wind speed heterogeneity (lighter shade is lower wind speed,
higher wind speed). The progress of each of the genetic individuals in the optimization process is
shown in the right-hand plot.
![](plot_complex_docs.png)

## Gridded layout optimization
The `LayoutOptimizationGridded` class allows users to quickly find a layout that fits the most
turbines possible into the specified boundary area, given that the turbines are arranged in a
gridded layout.
To do so, a range of different rotations and translations of a generic gridded arrangement are
tried, and the one that fits the most turbines into the boundary area is selected. No AEP
evaluations are performed; rather, the cost function $f$ to be maximized is simply $N$, the number
of turbines, and there is an additional constraint that the turbines are arranged in a gridded
fashion. Note that in other layout optimizers, $N$ is fixed.

We envisage that this will be useful for users that want to quickly generate a layout to
"fill" a boundary region in a gridded manner. By default, the gridded arrangement is a square grid
with spacing of `min_dist` (or `min_dist_D`); however, instantiating with the `hexagonal_packing`
keyword argument set to `True` will provide a grid that offsets the rows to enable tighter packing
of turbines while still satisfying the `min_dist`.

As with the `LayoutOptimizationRandomSearch` class, the boundaries specified can be complex (and
may contain separate areas).
User settings include `rotation_step`, which specifies the step size for rotating the grid
(in degrees); `rotation_range`, which specifies the range of rotation angles; `translation_step` or
`translation_step_D`, which specifies the step size for translating the grid in meters or rotor
diameters, respectively; and `translation_range`, which specifies the range of possible
translations. All come with default values, which we expect to be suitable for many or most users.
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Example: Gridded layout design
This example shows a layout optimization that places as many turbines as
possible into a given boundary using a gridded layout pattern.
"""

import matplotlib.pyplot as plt
import numpy as np

from floris import FlorisModel, WindRose
from floris.optimization.layout_optimization.layout_optimization_gridded import (
LayoutOptimizationGridded,
)


if __name__ == '__main__':
# Load the Floris model
fmodel = FlorisModel('../inputs/gch.yaml')

# Set the boundaries
# The boundaries for the turbines, specified as vertices
boundaries = [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)]

# Set up the optimization object with 5D spacing
layout_opt = LayoutOptimizationGridded(
fmodel,
boundaries,
min_dist_D=5., # results in spacing of 5*125.88 = 629.4 m
min_dist=None, # Alternatively, can specify spacing directly in meters
)

layout_opt.optimize()

# Note that the "initial" layout that is provided with the fmodel is
# not used by the layout optimization.
layout_opt.plot_layout_opt_results()

plt.show()
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""Example: Separated boundaries layout optimization
Demonstrates the capabilities of LayoutOptimizationGridded and
LayoutOptimizationRandomSearch to optimize turbine layouts with complex
boundaries.
"""

import matplotlib.pyplot as plt
import numpy as np

from floris import FlorisModel, WindRose
from floris.optimization.layout_optimization.layout_optimization_gridded import (
LayoutOptimizationGridded,
)
from floris.optimization.layout_optimization.layout_optimization_random_search import (
LayoutOptimizationRandomSearch,
)


if __name__ == '__main__':
# Load the Floris model
fmodel = FlorisModel('../inputs/gch.yaml')

# Set the boundaries
# The boundaries for the turbines, specified as vertices
boundaries = [
[(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)],
[(1500.0, 0.0), (1500.0, 1000.0), (2500.0, 0.0), (1500.0, 0.0)],
]

# Set up the wind data information
wind_directions = np.arange(0, 360.0, 5.0)
np.random.seed(1)
wind_speeds = 8.0 + np.random.randn(1) * 0.0
# Shape frequency distribution to match number of wind directions and wind speeds
freq = (
np.abs(
np.sort(
np.random.randn(len(wind_directions))
)
)
.reshape( ( len(wind_directions), len(wind_speeds) ) )
)
freq = freq / freq.sum()
# Set wind data in the FlorisModel
fmodel.set(
wind_data=WindRose(
wind_directions=wind_directions,
wind_speeds=wind_speeds,
freq_table=freq,
ti_table=0.06
)
)

# Begin by placing as many turbines as possible using a gridded layout at 6D spacing
layout_opt_gridded = LayoutOptimizationGridded(
fmodel,
boundaries,
min_dist_D=6.,
min_dist=None,
)
layout_opt_gridded.optimize()
print("Gridded layout complete.")

# Set the layout on the fmodel
fmodel.set(layout_x=layout_opt_gridded.x_opt, layout_y=layout_opt_gridded.y_opt)

# Update the layout using a random search optimization with 5D minimum spacing
layout_opt_rs = LayoutOptimizationRandomSearch(
fmodel,
boundaries,
min_dist_D=5.,
seconds_per_iteration=10,
total_optimization_seconds=60.,
use_dist_based_init=False,
)
layout_opt_rs.optimize()

layout_opt_rs.plot_layout_opt_results(
initial_locs_plotting_dict={"label": "Gridded initial layout"},
final_locs_plotting_dict={"label": "Random search optimized layout"},
)

plt.show()
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ def __init__(
b_depth = list_depth(boundaries)

boundary_specification_error_msg = (
"boundaries should be a list of coordinates (specifed as (x,y) "+\
"tuples) or as a list of list of tuples (for seperable regions)."
"boundaries should be a list of coordinates (specified as (x,y) "+\
"tuples) or as a list of list of tuples (for separable regions)."
)

if b_depth == 1:
Expand Down Expand Up @@ -130,7 +130,14 @@ def optimize(self):
sol = self._optimize()
return sol

def plot_layout_opt_results(self, plot_boundary_dict={}, ax=None, fontsize=16):
def plot_layout_opt_results(
self,
plot_boundary_dict={},
initial_locs_plotting_dict={},
final_locs_plotting_dict={},
ax=None,
fontsize=16
):

x_initial, y_initial, x_opt, y_opt = self._get_initial_and_final_locs()

Expand All @@ -140,6 +147,7 @@ def plot_layout_opt_results(self, plot_boundary_dict={}, ax=None, fontsize=16):
ax = fig.add_subplot(111)
ax.set_aspect("equal")

# Handle default boundary plotting
default_plot_boundary_dict = {
"color":"None",
"alpha":1,
Expand All @@ -148,9 +156,30 @@ def plot_layout_opt_results(self, plot_boundary_dict={}, ax=None, fontsize=16):
}
plot_boundary_dict = {**default_plot_boundary_dict, **plot_boundary_dict}

# Handle default initial location plotting
default_initial_locs_plotting_dict = {
"marker":"o",
"color":"b",
"linestyle":"None",
"label":"Initial locations",
}
initial_locs_plotting_dict = {
**default_initial_locs_plotting_dict,
**initial_locs_plotting_dict
}

# Handle default final location plotting
default_final_locs_plotting_dict = {
"marker":"o",
"color":"r",
"linestyle":"None",
"label":"New locations",
}
final_locs_plotting_dict = {**default_final_locs_plotting_dict, **final_locs_plotting_dict}

self.plot_layout_opt_boundary(plot_boundary_dict, ax=ax)
ax.plot(x_initial, y_initial, "ob", label="Initial locations")
ax.plot(x_opt, y_opt, "or", label="New locations")
ax.plot(x_initial, y_initial, **initial_locs_plotting_dict)
ax.plot(x_opt, y_opt, **final_locs_plotting_dict)
ax.set_xlabel("x (m)", fontsize=fontsize)
ax.set_ylabel("y (m)", fontsize=fontsize)
ax.grid(True)
Expand Down
Loading
Loading