FLORIS Models#

This notebook provides information on the provided FlorisModels. Introductory Concepts introduced FlorisModel as the base class for all models in the FLORIS package. This notebook introduces the ParFlorisModel, UncertainFlorisModel, and ApproxFlorisModel classes, which are subclasses or compositions of FlorisModel.

Parallelized FLORIS Model#

The ParFlorisModel class is a subclass of FlorisModel that parallelizes the FLORIS calculations. This class is designed to have an interface that is the same as FlorisModel, but the calculations are parallelized.

Instantiation#

The ParFlorisModel class can be instantiated in the same way as the FlorisModel class, or else it can be instantiated by passing a FlorisModel object to the constructor.

from floris import FlorisModel, ParFlorisModel, TimeSeries
import numpy as np
import matplotlib.pyplot as plt

fmodel = FlorisModel("gch.yaml")

# Instantiation using yaml input file
pfmodel = ParFlorisModel("gch.yaml")

# Instantiation using fmodel
pfmodel = ParFlorisModel(fmodel)

Parameters#

The ParFlorisModel class has additional parameters the define the parallelization. These parameters are:

interface: The parallelization interface to use. Options are "multiprocessing", "pathos", and "concurrent", with possible future support for "mpi4py"

max_workers: The maximum number of workers to use. Defaults to -1, which then takes the number of CPUs available.

n_wind_condition_splits: The number of wind conditions to split the simulation over. Defaults to the same as max_workers.

return_turbine_powers_only: Whether to return only the turbine powers.

print_timings (bool): Print the computation time to the console. Defaults to False.

# Alternative parameters
pfmodel = ParFlorisModel(fmodel, max_workers=2)

Usage#

The ParFlorisModel class can be used in the same way as the FlorisModel class. The only difference is that the calculations are parallelized.


# Set to a two turbine layout
layout_x = [0, 500]
layout_y = [0, 0]
fmodel.set(layout_x=layout_x, layout_y=layout_y)
pfmodel.set(layout_x=layout_x, layout_y=layout_y)
wind_directions = np.arange(240, 300, 0.5)
time_series = TimeSeries(
    wind_directions=wind_directions, wind_speeds=8.0, turbulence_intensities=0.06
)
fmodel.set(wind_data=time_series)
pfmodel.set(wind_data=time_series)
fmodel.run()
pfmodel.run()

farm_power = fmodel.get_farm_power()
pfarm_power = pfmodel.get_farm_power()

# Show the results are the same
fig, ax = plt.subplots()
ax.plot(wind_directions, farm_power, label="FlorisModel", color='k', lw=5)
ax.plot(wind_directions, pfarm_power, label="ParFlorisModel", color='r', ls='--', lw=2)
ax.set_xlabel("Wind Direction (deg)")
ax.set_ylabel("Farm Power (kW)")
ax.legend()
ax.grid()
_images/b323b428cd1defbccc112237b1110db70ff02f695ab647b8f3bf9ed81553659d.png

UncertainFlorisModel#

The UncertainFlorisModel class is a composition of FlorisModel that adds uncertainty to the input conditions. Its interface is meant to made similar to FlorisModel, but with the addition of uncertainty in wind direction.

Instantiation#

The UncertainFlorisModel class can be instantiated in the same way as the FlorisModel class, or else it can be instantiated by passing a FlorisModel object to the constructor. Alternatively a ParFlorisModel object can be passed to the constructor which ensures the underlying calculations are parallelized according to the ParFlorisModel parameters.

from floris import UncertainFlorisModel

# Instantiation options
ufmodel = UncertainFlorisModel("gch.yaml") # Using input yaml
ufmodel = UncertainFlorisModel(fmodel) # Using a FlorisModel object
ufmodel = UncertainFlorisModel(pfmodel) # Using a ParFlorisModel object

Parameters#

To include uncertainty into the wind direction, the UncertainFlorisModel class, for each findex run, the result for a wind direction is provided by performing a Gaussian blend over results from multiple wind directions nearby wind directions. To reduce the total number of calculations required, a resolution of wind direction, wind speed, turbulence intensity and control inputs are specified and repeated calculations are only calculated once. See the class API for complete details but some key parameters are:

wd_resolution, ws_resolution, ti_resolution, yaw_resolution, and power_setpoint_resolution: Define the granularity of calculations for wind direction, wind speed, turbulence intensity, yaw angle, and power setpoints, respectively.

wd_std: The standard deviation of wind direction, used in the Gaussian blending.

wd_sample_points: Specific wind direction points to sample for expanded conditions.

# Define the uncertainty to have a wd_std of 5 degrees and blend over 10 degrees
ufmodel = UncertainFlorisModel(fmodel, wd_std=5, wd_sample_points=[-5, -4, -3, -2, -1, 0, 1, 2,3, 4, 5], wd_resolution=0.5)

Usage#

Usage of UncertainFlorisModel is similar to FlorisModel however the results will now include the effects of Gaussian blending

ufmodel.set(wind_data=time_series, layout_x=layout_x, layout_y=layout_y)
ufmodel.run()

# Get the power of the downstream turbine
f_power = fmodel.get_turbine_powers()[:,1]
uf_power = ufmodel.get_turbine_powers()[:,1]

# Plot the two powers
fig, ax = plt.subplots()
ax.plot(wind_directions, f_power, label="FlorisModel", color='k', lw=5)
ax.plot(wind_directions, uf_power, label="UncertainFlorisModel", color='r', lw=2)
ax.set_xlabel("Wind Direction (deg)")
ax.set_ylabel("Turbine Power (kW)")
ax.legend()
ax.grid()
_images/9725b2d2da313e5b7581536f86f5d495dbdaa304eab3e428a162909fbdabec6f.png

ApproxFlorisModel#

The ApproxFlorisModel overloads the UncertainFlorisModel with the special case that the wd_sample_points = [0]. This is a special case where no uncertainty is added but the resolution of the values wind direction, wind speed etc are still reduced by the specified resolution. This allows for cases to be reused and a faster approximate result computed

Instantiation#

ApproxFlorisModel can be instantiated in the same way as the UncertainFlorisModel class

from floris import ApproxFlorisModel

# Instantiation options
amodel = ApproxFlorisModel("gch.yaml") # Using input yaml
amodel = ApproxFlorisModel(fmodel) # Using a FlorisModel object
amodel = ApproxFlorisModel(pfmodel) # Using a ParFlorisModel object

Usage#

ApproxFlorisModel is used in the same way as UncertainFlorisModel but with the special case that the wd_sample_points = [0]. This means that while the resolution of the values wind_direction, wind_speeds etc are still reduced by the specified resolution, no uncertainty is added. It is intended for quickly processing large sets of inflow conditions (e.g., when reproducing SCADA records), when approximate solutions are sufficient.

# Instantiate with a wind speed resolution of 0.5 m/s and a wind direction resolution of 1 degree
amodel = ApproxFlorisModel(fmodel, ws_resolution=0.5, wd_resolution=1.0) 
# Show approximation for a time series including smaller increments of wind speed
wind_speeds = np.arange(6, 10, 0.1)
time_series = TimeSeries(
    wind_directions = 270.0,
    wind_speeds=wind_speeds,
    turbulence_intensities=0.06
)
amodel.set(wind_data=time_series)

print(f"amodel has an n_findex of {amodel.n_findex}, ",
      f"however, the number of unique cases to run at this resolution is {amodel.n_unique}.")

amodel.run()
farm_power = amodel.get_farm_power()
amodel has an n_findex of 40,  however, the number of unique cases to run at this resolution is 9.
# Show the farm power over wind speeds
fig, ax = plt.subplots()
ax.plot(wind_speeds, farm_power, label="ApproxFlorisModel", color='k', lw=2)
ax.legend()
ax.set_xlabel("Wind Speed (m/s)")
ax.set_ylabel("Farm Power (kW)")
ax.grid()
_images/0db180e70221cd795f0630881ab79a8ac3473e31860192e9f7c1ee8fc4e88478.png