-
Notifications
You must be signed in to change notification settings - Fork 156
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Randomized layout optimization (#697)
* Add random optimization class * Add temporary example * rename example * Adding support for arbitrary pmf. * Plotting for pmf; update example. * Respecting new examples in develop. * Update base class to handle multiple regions of layout optimization. * geoyaw option added. * Terminology change: particle -> individual. * Adding functionality for plotting the optimization boundary only. * Adding grid. * Adding visualizations. * Allow random seed setting; non-parallel runs; log status. * Updating link to Stanleys published paper. * Enabling geometric yaw option. * WIP; storing for hotfix. * Bug fixed: updating layout when called. * Ruff * fi_subset needed updating rather than fi. * WIP commit to change branch. * runs. * Store initial aep correctly. * Minor update to defualt pmf' * Runs, but freq data not passed correctly. * Pipe through wind_data for freq information. * Move example to subdirectory. * Ruff. * Working on adding tests; still some work to do on value optimization. * Enable value optimization. * Handling TODO items * UserWarning -> ValueError; logger.warnings * Simple documentation added. * White space in docs. * Remove cm.get_cmap in favor of plt.get_cmap * Improve documentation and example. * renable geometric yaw option for v4; fix scipy layoutopt args. * remove self.x and self.y after initialization. * Improve progress plots and add to example. --------- Co-authored-by: misi9170 <[email protected]>
- Loading branch information
Showing
13 changed files
with
1,202 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
|
||
(layout_optimization)= | ||
# Layout optimization | ||
|
||
The FLORIS package provides layout optimization tools to place turbines within a specified | ||
boundary area to optimize annual energy production (AEP) or wind plant value. Layout | ||
optimizers accept an instantiated `FlorisModel` and alter the turbine layouts in order to | ||
improve the objective function value (AEP or value). | ||
|
||
## Background | ||
|
||
Layout optimization entails placing turbines in a wind farm in a configuration that maximizes | ||
an objective function, usually the AEP. Turbines are moved to minimize their wake interactions | ||
in the most dominant wind directions, while respecting the boundaries of the area for turbine | ||
placement as well as minimum distance requirements between neighboring turbines. | ||
|
||
Mathematically, we represent this as a (nonconvex) optimization problem. | ||
Let $x = \{x_i\}_{i=1,\dots,N}$, $x_i \in \mathbb{R}^2$ represent the set of | ||
coordinates of turbines within a farm (that is, $x_i$ represents the $(X, Y)$ | ||
location of turbine $i$). Further, let $R \subset \mathbb{R}^2$ be a closed | ||
region in which to place turbines. Finally, let $d$ represent the minimum | ||
allowable distance between two turbines. Then, the layout optimization problem | ||
is expressed as | ||
|
||
$$ | ||
\begin{aligned} | ||
\underset{x}{\text{maximize}} & \:\: f(x)\\ | ||
\text{subject to} & \:\: x \subset R \\ | ||
& \:\: ||x_i - x_j|| \geq d \:\: \forall j = 1,\dots,N, j\neq i | ||
\end{aligned} | ||
$$ | ||
|
||
Here, $||\cdot||$ denotes the Euclidean norm, and $f(x)$ is the cost function to be maximized. | ||
|
||
When maximizing the AEP, $f = \sum_w P(w, x)p_W(w)$, where $w$ is the wind condition bin | ||
(e.g., wind speed, wind direction pair); $P(w, x)$ is the power produced by the wind farm in | ||
condition $w$ with layout $x$; and $p_W(w)$ is the annual frequency of occurrence of | ||
condition $w$. | ||
|
||
Layout optimizers take iterative approaches to solving the layout optimization problem | ||
specified above. Optimization routines available in FLORIS are described below. | ||
|
||
## Scipy layout optimization | ||
The `LayoutOptimizationScipy` class is built around `scipy.optimize`s `minimize` | ||
routine, using the `SLSQP` solver by default. Options for adjusting | ||
`minimize`'s behavior are exposed to the user with the `optOptions` argument. | ||
Other options include enabling fast wake steering at each layout optimizer | ||
iteration with the `enable_geometric_yaw` argument, and switch from AEP | ||
optimization to value optimization with the `use_value` argument. | ||
|
||
## Genetic random search layout optimization | ||
The `LayoutOptimizationRandomSearch` class is a custom optimizer designed specifically for | ||
layout optimization via random perturbations of the turbine locations. It is designed to have | ||
the following features: | ||
- Robust to complex wind conditions and complex boundaries, including disjoint regions for | ||
turbine placement | ||
- Easy to parallelize and wrapped in a genetic algorithm for propagating candidate solutions | ||
- Simple to set up and tune for non-optimization experts | ||
- Set up to run cheap constraint checks prior to more expensive objective function evaluations | ||
to accelerate optimization | ||
|
||
The algorithm, described in full in an upcoming paper that will be linked here when it is | ||
publicly available, moves a random turbine and random distance in a random direction; checks | ||
that constraints are satisfied; evaluates the objective function (AEP or value); and then | ||
commits to the move if there is an objective function improvement. The main tuning parameter | ||
is the probability mass function for the random movement distance, which is a dictionary to be | ||
passed to the `distance_pmf` argument. | ||
|
||
The `distance_pmf` dictionary should contain two keys, each containing a 1D array of equal | ||
length: `"d"`, which specifies the perturbation distance _in units of the rotor diameter_, | ||
and `"p"`, which specifies the probability that the corresponding perturbation distance is | ||
chosen at any iteration of the random search algorithm. The `distance_pmf` can therefore be | ||
used to encourage or discourage more aggressive or more conservative movements, and to enable | ||
or disable jumps between disjoint regions for turbine placement. | ||
|
||
The figure below shows an example of the optimized layout of a farm using the GRS algorithm, with | ||
the black dots indicating the initial layout; red dots indicating the final layout; and blue | ||
shading indicating wind speed heterogeneity (lighter shade is lower wind speed, darker shade is | ||
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) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
82 changes: 82 additions & 0 deletions
82
examples/examples_layout_optimization/003_genetic_random_search.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
"""Example: Layout optimization with genetic random search | ||
This example shows a layout optimization using the genetic random search | ||
algorithm. It provides options for the users to try different distance | ||
probability mass functions for the random search perturbations. | ||
""" | ||
|
||
import matplotlib.pyplot as plt | ||
import numpy as np | ||
from scipy.stats import gamma | ||
|
||
from floris import FlorisModel, WindRose | ||
from floris.optimization.layout_optimization.layout_optimization_random_search import ( | ||
LayoutOptimizationRandomSearch, | ||
) | ||
|
||
|
||
if __name__ == '__main__': | ||
# Set up FLORIS | ||
fmodel = FlorisModel('../inputs/gch.yaml') | ||
|
||
|
||
# Setup 72 wind directions with a random wind speed and frequency distribution | ||
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() | ||
fmodel.set( | ||
wind_data=WindRose( | ||
wind_directions=wind_directions, | ||
wind_speeds=wind_speeds, | ||
freq_table=freq, | ||
ti_table=0.06 | ||
) | ||
) | ||
|
||
# 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 turbine locations to 4 turbines in a rectangle | ||
D = 126.0 # rotor diameter for the NREL 5MW | ||
layout_x = [0, 0, 6 * D, 6 * D] | ||
layout_y = [0, 4 * D, 0, 4 * D] | ||
fmodel.set(layout_x=layout_x, layout_y=layout_y) | ||
|
||
# Perform the optimization | ||
distance_pmf = None | ||
|
||
# Other options that users can try | ||
# 1. | ||
# distance_pmf = {"d": [100, 1000], "p": [0.8, 0.2]} | ||
# 2. | ||
# p = gamma.pdf(np.linspace(0, 900, 91), 15, scale=20); p = p/p.sum() | ||
# distance_pmf = {"d": np.linspace(100, 1000, 91), "p": p} | ||
|
||
layout_opt = LayoutOptimizationRandomSearch( | ||
fmodel, | ||
boundaries, | ||
min_dist_D=5., | ||
seconds_per_iteration=10, | ||
total_optimization_seconds=60., | ||
distance_pmf=distance_pmf | ||
) | ||
layout_opt.describe() | ||
layout_opt.plot_distance_pmf() | ||
|
||
layout_opt.optimize() | ||
|
||
layout_opt.plot_layout_opt_results() | ||
|
||
layout_opt.plot_progress() | ||
|
||
plt.show() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.