Example: Layout optimization with WindRoseWRG comparison

Example: Layout optimization with WindRoseWRG comparison#

"""Example: Layout optimization with WindRoseWRG comparison

This example compares a layout optimization using a WindRoseWRG.  In the example, two
turbine positions are optimized within a square grid.  The optimization is run 3 times:

1. Using a WindRoseWRG object generated using the example wrg file
2. Using a WindRose object created from the WindRoseWRG object
3. Using a WindRose object created from the WindRoseWRG object, but with the HeterogeneousMap
   also generated by the WindRoseWRG object


Because the WRG file includes a speed-up for northern winds, optimizaitions (1) and (3) place both
turbines near the northern boundary of the grid, while optimization (2) places the turbines in the
furthest corners of the grid.  The optimization illustrates that using the full WindRoseWRG object
produces a similar result to the WindRose object with the HeterogeneousMap, since they both
can represent the difference in resource for different locations.  The HeterogeneousMap may have
advantage for larger cases since it is running with only 1 wind speed.  For this example the results
and performance are similar.

"""

import matplotlib.pyplot as plt
import numpy as np

from floris import (
    FlorisModel,
    WindRoseWRG,
)
from floris.optimization.layout_optimization.layout_optimization_random_search import (
    LayoutOptimizationRandomSearch,
)


if __name__ == "__main__":

    # Parameters of layout optimization
    seconds_per_iteration = 30.0
    total_optimization_seconds = 120.0
    min_dist_D = 3.0
    use_dist_based_init = False

    # Initialize the WindRoseWRG object with wind speeds every 2 m/s and fixed ti of 6%.  Specify
    # a wd_step of 4 degrees, which implies upsampling from wrg's 90 degree sectors to 12
    # degree sectors
    wind_rose_wrg = WindRoseWRG(
        "wrg_example.wrg",
        wd_step=2.0,
        wind_speeds=np.arange(0, 21, 1.0),  # Use a sparser range of speeds
        ti_table=0.06,
    )

    # Define an optimization boundary within the grid that is a square with a
    # buffer to avoid the outer limits of the heterogeneous area
    buffer = 100.0
    boundaries = [
        (buffer, buffer),
        (1000 - buffer, buffer),
        (1000 - buffer, 2000 - buffer),
        (buffer, 2000 - buffer),
        (buffer, buffer),
    ]

    # Select and initial layout in the corners of the boundary
    layout_x = np.array([500, 1000 - buffer])
    layout_y = np.array([900, 2000 - buffer])

    ##########################
    # Set up the FlorisModel
    fmodel = FlorisModel("../inputs/gch.yaml")

    ##########################
    # Use the get_heterogeneous_map method to generate a WindRose that represents
    # the information in the WindRoseWRG, rather than a set of WindRose objects
    # but as a  single WindRose object (for one location) and a HeterogeneousMap
    # the describes the speed up information per direction across the domain
    # This will allow running the optimization for a single wind speed while still
    # accounting for the difference in wind speeds in location by direction
    wind_rose_het = wind_rose_wrg.get_heterogeneous_wind_rose(
        fmodel=fmodel,
        x_loc=0.0,
        y_loc=0.0,
        representative_wind_speed=9.0,
    )

    # Pull out the heterogeneous plot to show the underlying speedups
    het_map = wind_rose_het.heterogeneous_map
    wind_direction_to_plot = [0.0, 10.0, 45.0, 75.0, 90.0, 180.0]

    # Show the het_map for a few wind directions
    fig, axarr = plt.subplots(1, len(wind_direction_to_plot), figsize=(16, 5))
    axarr = axarr.flatten()
    for i, wd in enumerate(wind_direction_to_plot):
        het_map.plot_single_speed_multiplier(
            wind_direction=wd,
            wind_speed=8.0,
            ax=axarr[i],
            show_colorbar=True,
        )

        axarr[i].set_title(f"Wind Direction: {wd}")

    # ##########################
    # Run the optimization with the full WindRoseWRG first
    fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=wind_rose_wrg)

    # Set the layout optimization
    layout_opt = LayoutOptimizationRandomSearch(
        fmodel,
        boundaries,
        min_dist_D=min_dist_D,
        seconds_per_iteration=seconds_per_iteration,
        total_optimization_seconds=total_optimization_seconds,
        use_dist_based_init=use_dist_based_init,
    )

    layout_opt.optimize()
    x_initial, y_initial, x_opt_wrg, y_opt_wrg = layout_opt._get_initial_and_final_locs()

    # Grab the log array
    objective_log_array_wrg = np.array(layout_opt.objective_candidate_log)

    # Normalize
    objective_log_array_wrg = objective_log_array_wrg / np.max(objective_log_array_wrg)

    print("=====================================")
    print("Objective log array (WRG):")
    print(objective_log_array_wrg.shape)
    print(objective_log_array_wrg)

    # ##########################
    # Repeat using wind_rose_het
    fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=wind_rose_het)

    # Set the layout optimization
    layout_opt = LayoutOptimizationRandomSearch(
        fmodel,
        boundaries,
        min_dist_D=min_dist_D,
        seconds_per_iteration=seconds_per_iteration,
        total_optimization_seconds=total_optimization_seconds,
        use_dist_based_init=use_dist_based_init,
    )

    layout_opt.optimize()
    _, _, x_opt_het, y_opt_het = layout_opt._get_initial_and_final_locs()

    # Grab the log array
    objective_log_array_het = np.array(layout_opt.objective_candidate_log)

    # Normalize
    objective_log_array_het = objective_log_array_het / np.max(objective_log_array_het)

    # ##########################
    # Repeat using single wind rose (without het)
    wind_rose = wind_rose_wrg.get_wind_rose_at_point(0, 0)
    fmodel = FlorisModel("../inputs/gch.yaml")
    fmodel.set(layout_x=layout_x, layout_y=layout_y, wind_data=wind_rose)

    # Set the layout optimization
    layout_opt = LayoutOptimizationRandomSearch(
        fmodel,
        boundaries,
        min_dist_D=min_dist_D,
        seconds_per_iteration=seconds_per_iteration,
        total_optimization_seconds=total_optimization_seconds,
        use_dist_based_init=use_dist_based_init,
    )

    layout_opt.optimize()
    _, _, x_opt_wr, y_opt_wr = layout_opt._get_initial_and_final_locs()

    # Grab the log array
    objective_log_array_wr = np.array(layout_opt.objective_candidate_log)

    # Normalize
    objective_log_array_wr = objective_log_array_wr / np.max(objective_log_array_wr)

    fig, ax = plt.subplots(1, 1, figsize=(8, 8))
    layout_opt.plot_layout_opt_boundary(ax=ax)
    ax.scatter(x_initial, y_initial, label="Initial Layout", s=80, color="k", marker="s")
    ax.scatter(
        x_opt_wr, y_opt_wr, label="Optimized Layout (Single Wind Rose)", s=60, color="b", marker="^"
    )
    ax.scatter(x_opt_wrg, y_opt_wrg, label="Optimized Layout (WRG)", s=40, color="r", marker="o")
    ax.scatter(
        x_opt_het,
        y_opt_het,
        label="Optimized Layout (Single Wind Rose + Het)",
        s=20,
        color="g",
        marker="h",
    )
    ax.set_aspect('equal')
    ax.legend()

    print("=====================================")
    print("Objective log array (HET):")
    print(objective_log_array_het.shape)
    print(objective_log_array_het)

    fig, ax = plt.subplots(1, 1, figsize=(8, 8))
    for objective_log_array, label, color in zip(
        [objective_log_array_wr, objective_log_array_wrg, objective_log_array_het],
        ["WR", "WRG", "Het"],
        ["b", "r", "g"],
    ):
        ax.plot(
            np.arange(len(objective_log_array)),
            np.log10(objective_log_array * 100.0),
            label=label,
            color=color,
        )
        ax.set_xlabel("Iteration")
        ax.set_ylabel("Objective")
        ax.legend()

    plt.show()
import warnings
warnings.filterwarnings('ignore')
/home/runner/work/floris/floris/floris/core/wake_deflection/gauss.py:328: RuntimeWarning: invalid value encountered in divide
  val = 2 * (avg_v - v_core) / (v_top + v_bottom)
/home/runner/work/floris/floris/floris/core/wake_deflection/gauss.py:163: RuntimeWarning: invalid value encountered in divide
  C0 = 1 - u0 / freestream_velocity
/home/runner/work/floris/floris/floris/core/wake_velocity/gauss.py:80: RuntimeWarning: invalid value encountered in divide
  sigma_z0 = rotor_diameter_i * 0.5 * np.sqrt(uR / (u_initial + u0))
/home/runner/work/floris/floris/floris/wind_data.py:2960: RuntimeWarning: invalid value encountered in power
  exponent = -((x / a) ** k)
Using point 0 at (0.0, 0.0) as reference location
/home/runner/work/floris/floris/floris/core/wake_deflection/gauss.py:498: RuntimeWarning: invalid value encountered in divide
  I_total = np.sqrt((2 / 3) * k_total) / average_u_i
Using supplied initial layout for 4 individuals.
=======================================
Optimization step +0.0
Optimization time = +0.0 [s]
Mean AEP = 40.6 [GWh] (+0.00%)
Median AEP = 40.6 [GWh] (+0.00%)
Max AEP = 40.6 [GWh] (+0.00%)
Min AEP = 40.6 [GWh] (+0.00%)
=======================================
Optimizing using 4 individuals.
Optimization time: 0.0 s / 120.0 s
=======================================
Optimization step +1.0
Optimization time = +30.5 [s]
Mean AEP = 40.8 [GWh] (+0.48%)
Median AEP = 40.8 [GWh] (+0.48%)
Max AEP = 40.9 [GWh] (+0.58%)
Min AEP = 40.8 [GWh] (+0.40%)
=======================================
Optimization time: 30.5 s / 120.0 s
=======================================
Optimization step +2.0
Optimization time = +60.9 [s]
Mean AEP = 40.9 [GWh] (+0.54%)
Median AEP = 40.9 [GWh] (+0.58%)
Max AEP = 40.9 [GWh] (+0.58%)
Min AEP = 40.8 [GWh] (+0.41%)
=======================================
Optimization time: 60.9 s / 120.0 s
=======================================
Optimization step +3.0
Optimization time = +91.2 [s]
Mean AEP = 40.9 [GWh] (+0.58%)
Median AEP = 40.9 [GWh] (+0.58%)
Max AEP = 40.9 [GWh] (+0.58%)
Min AEP = 40.9 [GWh] (+0.57%)
=======================================
Optimization time: 91.2 s / 120.0 s
=======================================
Optimization step +4.0
Optimization time = +121.5 [s]
Mean AEP = 40.9 [GWh] (+0.58%)
Median AEP = 40.9 [GWh] (+0.58%)
Max AEP = 40.9 [GWh] (+0.58%)
Min AEP = 40.9 [GWh] (+0.58%)
=======================================
Final AEP = 40.9 [GWh] (+0.58%)
=====================================
Objective log array (WRG):
(5, 4)
[[0.99421077 0.99421077 0.99421077 0.99421077]
 [0.99964027 0.99828345 0.99821184 0.99998553]
 [0.9999953  0.99989472 0.9982904  0.99998553]
 [0.9999953  0.99998553 0.99989472 0.9999953 ]
 [0.9999953  1.         0.99998553 0.9999953 ]]
Using supplied initial layout for 4 individuals.
=======================================
Optimization step +0.0
Optimization time = +0.0 [s]
Mean AEP = 43.7 [GWh] (+0.00%)
Median AEP = 43.7 [GWh] (+0.00%)
Max AEP = 43.7 [GWh] (+0.00%)
Min AEP = 43.7 [GWh] (+0.00%)
=======================================
Optimizing using 4 individuals.
Optimization time: 0.0 s / 120.0 s
=======================================
Optimization step +1.0
Optimization time = +30.3 [s]
Mean AEP = 44.0 [GWh] (+0.66%)
Median AEP = 44.0 [GWh] (+0.67%)
Max AEP = 44.0 [GWh] (+0.74%)
Min AEP = 44.0 [GWh] (+0.58%)
=======================================
Optimization time: 30.3 s / 120.0 s
=======================================
Optimization step +2.0
Optimization time = +60.5 [s]
Mean AEP = 44.0 [GWh] (+0.74%)
Median AEP = 44.0 [GWh] (+0.74%)
Max AEP = 44.0 [GWh] (+0.76%)
Min AEP = 44.0 [GWh] (+0.72%)
=======================================
Optimization time: 60.5 s / 120.0 s
=======================================
Optimization step +3.0
Optimization time = +90.8 [s]
Mean AEP = 44.0 [GWh] (+0.75%)
Median AEP = 44.0 [GWh] (+0.76%)
Max AEP = 44.0 [GWh] (+0.76%)
Min AEP = 44.0 [GWh] (+0.74%)
=======================================
Optimization time: 90.8 s / 120.0 s
=======================================
Optimization step +4.0
Optimization time = +121.0 [s]
Mean AEP = 44.0 [GWh] (+0.76%)
Median AEP = 44.0 [GWh] (+0.76%)
Max AEP = 44.0 [GWh] (+0.76%)
Min AEP = 44.0 [GWh] (+0.76%)
=======================================
Final AEP = 44.0 [GWh] (+0.76%)
Using supplied initial layout for 4 individuals.
=======================================
Optimization step +0.0
Optimization time = +0.0 [s]
Mean AEP = 40.1 [GWh] (+0.00%)
Median AEP = 40.1 [GWh] (+0.00%)
Max AEP = 40.1 [GWh] (+0.00%)
Min AEP = 40.1 [GWh] (+0.00%)
=======================================
Optimizing using 4 individuals.
Optimization time: 0.0 s / 120.0 s
=======================================
Optimization step +1.0
Optimization time = +30.3 [s]
Mean AEP = 40.3 [GWh] (+0.62%)
Median AEP = 40.3 [GWh] (+0.63%)
Max AEP = 40.3 [GWh] (+0.64%)
Min AEP = 40.3 [GWh] (+0.59%)
=======================================
Optimization time: 30.3 s / 120.0 s
=======================================
Optimization step +2.0
Optimization time = +60.6 [s]
Mean AEP = 40.3 [GWh] (+0.65%)
Median AEP = 40.3 [GWh] (+0.65%)
Max AEP = 40.3 [GWh] (+0.65%)
Min AEP = 40.3 [GWh] (+0.65%)
=======================================
Optimization time: 60.6 s / 120.0 s
=======================================
Optimization step +3.0
Optimization time = +90.9 [s]
Mean AEP = 40.3 [GWh] (+0.65%)
Median AEP = 40.3 [GWh] (+0.65%)
Max AEP = 40.3 [GWh] (+0.65%)
Min AEP = 40.3 [GWh] (+0.65%)
=======================================
Optimization time: 90.9 s / 120.0 s
=======================================
Optimization step +4.0
Optimization time = +121.1 [s]
Mean AEP = 40.3 [GWh] (+0.65%)
Median AEP = 40.3 [GWh] (+0.65%)
Max AEP = 40.3 [GWh] (+0.65%)
Min AEP = 40.3 [GWh] (+0.65%)
=======================================
Final AEP = 40.3 [GWh] (+0.65%)
=====================================
Objective log array (HET):
(5, 4)
[[0.99245422 0.99245422 0.99245422 0.99245422]
 [0.99979663 0.99839982 0.9981785  0.99974663]
 [0.99979663 0.99974663 0.9996236  0.99997482]
 [0.9999799  0.9999748  0.9997788  0.99997482]
 [1.         0.99997482 0.9999748  0.99999837]]
../../_images/99f05b803e022241b68b8d6211b47329a58d8072aff78cc9c2355367b182c115.png ../../_images/84b72e005dc1636ca478154eb85262ca459278cea001d2277c28785dc67c1dda.png ../../_images/3f89512606870d6404472b1e0e71918d75f352513acad42061effd8684c00dc7.png