Skip to content

Commit

Permalink
Move FlorisInterface .reinitialize() / .calculate_wake() to .set() / …
Browse files Browse the repository at this point in the history
….run() (NREL#823)

* Main infrastrcuture for set() -> run() paradigm on FlorisInterface.

* calculate plane methods updated; 02 example runs.

* Ruff.

* Updating examples that called reinitialize() and calculate_wake() directly.

* Update AEP methods

* yaw optimizer updates, and simpler yaw opt examples.

* Examples running with get_farm_AEP.

* Layout optimizers.

* update reinitialize calls in sample_velocity_deficit_profiles.

* Initial working implementation; update to follow.

* minimize unnecessary set() calls.

* tests for FlorisInterfacee updated; added reset_operation() method to FlorisInterface.

* Simple docstrings added.

* isort.

* adding WindTIRose class, which includes wd, ws, and ti as wind rose dimensions

* Fix a missed calculate_no_wake() call.

* adding tests for WindTIRose

* formatting wind data

* Test set / run sequences

* Update other test api’s

* Fix line length

* Small edits to comments

* fixing wind rose example plots

* Remove unused input args

* Refactor and clean up

* Bug fix in tests

* Error if calculate_wake or reinitialize are used

* Fix the bug in test_disable_turbines

* Fix whitespace

* Fix typo and docstrings

* Update docstring

* Fix formatting

* Raise error if run() called on ParallelComputingInterface.

---------

Co-authored-by: misi9170 <[email protected]>
Co-authored-by: Eric Simley <[email protected]>
Co-authored-by: Paul <[email protected]>
Co-authored-by: ejsimley <[email protected]>
  • Loading branch information
5 people authored Feb 27, 2024
1 parent d3631fa commit dc1b570
Show file tree
Hide file tree
Showing 50 changed files with 659 additions and 479 deletions.
20 changes: 9 additions & 11 deletions examples/01_opening_floris_computing_power.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,16 @@
fi = FlorisInterface("inputs/gch.yaml")

# Convert to a simple two turbine layout
fi.reinitialize(layout_x=[0, 500.0], layout_y=[0.0, 0.0])
fi.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0])

# Single wind speed and wind direction
print("\n========================= Single Wind Direction and Wind Speed =========================")

# Get the turbine powers assuming 1 wind direction and speed
fi.reinitialize(wind_directions=[270.0], wind_speeds=[8.0])
# Set the yaw angles to 0 with 1 wind direction and speed
fi.set(wind_directions=[270.0], wind_speeds=[8.0], yaw_angles=np.zeros([1, 2]))

# Set the yaw angles to 0
yaw_angles = np.zeros([1, 2]) # 1 wind direction and speed, 2 turbines
fi.calculate_wake(yaw_angles=yaw_angles)
fi.run()

# Get the turbine powers
turbine_powers = fi.get_turbine_powers() / 1000.0
Expand All @@ -44,9 +43,9 @@
wind_speeds = np.array([8.0, 9.0, 10.0])
wind_directions = np.array([270.0, 270.0, 270.0])

fi.reinitialize(wind_speeds=wind_speeds, wind_directions=wind_directions)
yaw_angles = np.zeros([3, 2]) # 3 wind directions/ speeds, 2 turbines
fi.calculate_wake(yaw_angles=yaw_angles)
# 3 wind directions/ speeds
fi.set(wind_speeds=wind_speeds, wind_directions=wind_directions, yaw_angles=np.zeros([3, 2]))
fi.run()
turbine_powers = fi.get_turbine_powers() / 1000.0
print("The turbine power matrix should be of dimensions 3 findex X 2 Turbines")
print(turbine_powers)
Expand All @@ -60,9 +59,8 @@
wind_speeds = np.tile([8.0, 9.0, 10.0], 3)
wind_directions = np.repeat([260.0, 270.0, 280.0], 3)

fi.reinitialize(wind_directions=wind_directions, wind_speeds=wind_speeds)
yaw_angles = np.zeros([9, 2]) # 9 wind directions/ speeds, 2 turbines
fi.calculate_wake(yaw_angles=yaw_angles)
fi.set(wind_directions=wind_directions, wind_speeds=wind_speeds, yaw_angles=np.zeros([9, 2]))
fi.run()
turbine_powers = fi.get_turbine_powers() / 1000.0
print("The turbine power matrix should be of dimensions 9 WD/WS X 2 Turbines")
print(turbine_powers)
Expand Down
6 changes: 3 additions & 3 deletions examples/02_visualizations.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@

# Run the wake calculation to get the turbine-turbine interfactions
# on the turbine grids
fi.calculate_wake()
fi.run()

# Plot the values at each rotor
fig, axes, _ , _ = wakeviz.plot_rotor_values(
Expand All @@ -125,11 +125,11 @@
"type": "turbine_grid",
"turbine_grid_points": 10
}
fi.reinitialize(solver_settings=solver_settings)
fi.set(solver_settings=solver_settings)

# Run the wake calculation to get the turbine-turbine interfactions
# on the turbine grids
fi.calculate_wake()
fi.run()

# Plot the values at each rotor
fig, axes, _ , _ = wakeviz.plot_rotor_values(
Expand Down
4 changes: 2 additions & 2 deletions examples/03_making_adjustments.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@


# Change the wind shear, reset the wind speed, and plot a vertical slice
fi.reinitialize( wind_shear=0.2, wind_speeds=[8.0] )
fi.set(wind_shear=0.2, wind_speeds=[8.0])
y_plane = fi.calculate_y_plane(crossstream_dist=0.0)
wakeviz.visualize_cut_plane(
y_plane,
Expand All @@ -61,7 +61,7 @@
5.0 * fi.floris.farm.rotor_diameters[0,0] * np.arange(0, N, 1),
5.0 * fi.floris.farm.rotor_diameters[0,0] * np.arange(0, N, 1),
)
fi.reinitialize(layout_x=X.flatten(), layout_y=Y.flatten(), wind_directions=[270.0])
fi.set(layout_x=X.flatten(), layout_y=Y.flatten(), wind_directions=[270.0])
horizontal_plane = fi.calculate_horizontal_plane(height=90.0)
wakeviz.visualize_cut_plane(
horizontal_plane,
Expand Down
7 changes: 4 additions & 3 deletions examples/04_sweep_wind_directions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
D = 126.
layout_x = np.array([0, D*6])
layout_y = [0, 0]
fi.reinitialize(layout_x=layout_x, layout_y=layout_y)
fi.set(layout_x=layout_x, layout_y=layout_y)

# Sweep wind speeds but keep wind direction fixed
wd_array = np.arange(250,291,1.)
ws_array = 8.0 * np.ones_like(wd_array)
fi.reinitialize(wind_directions=wd_array, wind_speeds=ws_array)
fi.set(wind_directions=wd_array, wind_speeds=ws_array)

# Define a matrix of yaw angles to be all 0
# Note that yaw angles is now specified as a matrix whose dimensions are
Expand All @@ -37,9 +37,10 @@
n_findex = num_wd # Could be either num_wd or num_ws
num_turbine = len(layout_x) # Number of turbines
yaw_angles = np.zeros((n_findex, num_turbine))
fi.set(yaw_angles=yaw_angles)

# Calculate
fi.calculate_wake(yaw_angles=yaw_angles)
fi.run()

# Collect the turbine powers
turbine_powers = fi.get_turbine_powers() / 1E3 # In kW
Expand Down
7 changes: 4 additions & 3 deletions examples/05_sweep_wind_speeds.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
D = 126.
layout_x = np.array([0, D*6])
layout_y = [0, 0]
fi.reinitialize(layout_x=layout_x, layout_y=layout_y)
fi.set(layout_x=layout_x, layout_y=layout_y)

# Sweep wind speeds but keep wind direction fixed
ws_array = np.arange(5,25,0.5)
wd_array = 270.0 * np.ones_like(ws_array)
fi.reinitialize(wind_directions=wd_array,wind_speeds=ws_array)
fi.set(wind_directions=wd_array,wind_speeds=ws_array)

# Define a matrix of yaw angles to be all 0
# Note that yaw angles is now specified as a matrix whose dimensions are
Expand All @@ -37,9 +37,10 @@
n_findex = num_wd # Could be either num_wd or num_ws
num_turbine = len(layout_x)
yaw_angles = np.zeros((n_findex, num_turbine))
fi.set(yaw_angles=yaw_angles)

# Calculate
fi.calculate_wake(yaw_angles=yaw_angles)
fi.run()

# Collect the turbine powers
turbine_powers = fi.get_turbine_powers() / 1E3 # In kW
Expand Down
7 changes: 4 additions & 3 deletions examples/06_sweep_wind_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
D = 126.0
layout_x = np.array([0, D*6, D*12, D*18, D*24])
layout_y = [0, 0, 0, 0, 0]
fi.reinitialize(layout_x=layout_x, layout_y=layout_y)
fi.set(layout_x=layout_x, layout_y=layout_y)

# In this case we want to check a grid of wind speed and direction combinations
wind_speeds_to_expand = np.arange(6, 9, 1.0)
Expand All @@ -46,7 +46,7 @@
wd_array = wind_directions_grid.flatten()

# Now reinitialize FLORIS
fi.reinitialize(wind_speeds=ws_array, wind_directions=wd_array)
fi.set(wind_speeds=ws_array, wind_directions=wd_array)

# Define a matrix of yaw angles to be all 0
# Note that yaw angles is now specified as a matrix whose dimensions are
Expand All @@ -56,9 +56,10 @@
n_findex = num_wd # Could be either num_wd or num_ws
num_turbine = len(layout_x)
yaw_angles = np.zeros((n_findex, num_turbine))
fi.set(yaw_angles=yaw_angles)

# Calculate
fi.calculate_wake(yaw_angles=yaw_angles)
fi.run()

# Collect the turbine powers
turbine_powers = fi.get_turbine_powers() / 1e3 # In kW
Expand Down
2 changes: 1 addition & 1 deletion examples/07_calc_aep_from_rose.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
# Assume a three-turbine wind farm with 5D spacing. We reinitialize the
# floris object and assign the layout, wind speed and wind direction arrays.
D = fi.floris.farm.rotor_diameters[0] # Rotor diameter for the NREL 5 MW
fi.reinitialize(
fi.set(
layout_x=[0.0, 5 * D, 10 * D],
layout_y=[0.0, 0.0, 0.0],
wind_directions=wind_directions,
Expand Down
10 changes: 5 additions & 5 deletions examples/09_compare_farm_power_with_neighbor.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,31 +24,31 @@
D = 126.
layout_x = np.array([0, D*6, 0, D*6])
layout_y = [0, 0, D*3, D*3]
fi.reinitialize(layout_x = layout_x, layout_y = layout_y)
fi.set(layout_x=layout_x, layout_y=layout_y)

# Define a simple wind rose with just 1 wind speed
wd_array = np.arange(0,360,4.)
ws_array = 8.0 * np.ones_like(wd_array)
fi.reinitialize(wind_directions=wd_array, wind_speeds=ws_array)
fi.set(wind_directions=wd_array, wind_speeds=ws_array)


# Calculate
fi.calculate_wake()
fi.run()

# Collect the farm power
farm_power_base = fi.get_farm_power() / 1E3 # In kW

# Add a neighbor to the east
layout_x = np.array([0, D*6, 0, D*6, D*12, D*15, D*12, D*15])
layout_y = np.array([0, 0, D*3, D*3, 0, 0, D*3, D*3])
fi.reinitialize(layout_x = layout_x, layout_y = layout_y)
fi.set(layout_x=layout_x, layout_y=layout_y)

# Define the weights to exclude the neighboring farm from calcuations of power
turbine_weights = np.zeros(len(layout_x), dtype=int)
turbine_weights[0:4] = 1.0

# Calculate
fi.calculate_wake()
fi.run()

# Collect the farm power with the neightbor
farm_power_neighbor = fi.get_farm_power(turbine_weights=turbine_weights) / 1E3 # In kW
Expand Down
2 changes: 1 addition & 1 deletion examples/10_opt_yaw_single_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
wd_array = np.arange(0.0, 360.0, 3.0)
ws_array = 8.0 * np.ones_like(wd_array)
D = 126.0 # Rotor diameter for the NREL 5 MW
fi.reinitialize(
fi.set(
layout_x=[0.0, 5 * D, 10 * D],
layout_y=[0.0, 0.0, 0.0],
wind_directions=wd_array,
Expand Down
2 changes: 1 addition & 1 deletion examples/11_opt_yaw_multiple_ws.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

# Reinitialize as a 3-turbine farm with range of WDs and WSs
D = 126.0 # Rotor diameter for the NREL 5 MW
fi.reinitialize(
fi.set(
layout_x=[0.0, 5 * D, 10 * D],
layout_y=[0.0, 0.0, 0.0],
wind_directions=wd_array,
Expand Down
10 changes: 5 additions & 5 deletions examples/12_optimize_yaw.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def load_floris():
5.0 * fi.floris.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1),
5.0 * fi.floris.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1),
)
fi.reinitialize(layout_x=X.flatten(), layout_y=Y.flatten())
fi.set(layout_x=X.flatten(), layout_y=Y.flatten())

return fi

Expand Down Expand Up @@ -63,10 +63,10 @@ def calculate_aep(fi, df_windrose, column_name="farm_power"):
wd_array = np.array(df_windrose["wd"], dtype=float)
ws_array = np.array(df_windrose["ws"], dtype=float)
yaw_angles = np.array(df_windrose[yaw_cols], dtype=float)
fi.reinitialize(wind_directions=wd_array, wind_speeds=ws_array)
fi.set(wind_directions=wd_array, wind_speeds=ws_array, yaw_angles=yaw_angles)

# Calculate FLORIS for every WD and WS combination and get the farm power
fi.calculate_wake(yaw_angles)
fi.run()
farm_power_array = fi.get_farm_power()

# Now map FLORIS solutions to dataframe
Expand All @@ -90,7 +90,7 @@ def calculate_aep(fi, df_windrose, column_name="farm_power"):
# Load FLORIS
fi = load_floris()
ws_array = 8.0 * np.ones_like(fi.floris.flow_field.wind_directions)
fi.reinitialize(wind_speeds=ws_array)
fi.set(wind_speeds=ws_array)
nturbs = len(fi.layout_x)

# First, get baseline AEP, without wake steering
Expand All @@ -109,7 +109,7 @@ def calculate_aep(fi, df_windrose, column_name="farm_power"):
start_time = timerpc()
wd_array = np.arange(0.0, 360.0, 5.0)
ws_array = 8.0 * np.ones_like(wd_array)
fi.reinitialize(
fi.set(
wind_directions=wd_array,
wind_speeds=ws_array,
)
Expand Down
6 changes: 3 additions & 3 deletions examples/12_optimize_yaw_in_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def load_floris():
5.0 * fi.floris.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1),
5.0 * fi.floris.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1),
)
fi.reinitialize(layout_x=X.flatten(), layout_y=Y.flatten())
fi.set(layout_x=X.flatten(), layout_y=Y.flatten())

return fi

Expand Down Expand Up @@ -59,7 +59,7 @@ def load_windrose():
wd_array = wind_directions_grid.flatten()
ws_array = wind_speeds_grid.flatten()

fi_aep.reinitialize(
fi_aep.set(
wind_directions=wd_array,
wind_speeds=ws_array,
turbulence_intensities=[0.08], # Assume 8% turbulence intensity
Expand Down Expand Up @@ -112,7 +112,7 @@ def load_windrose():
wd_array_opt = wind_directions_grid.flatten()
ws_array_opt = wind_speeds_grid.flatten()

fi_opt.reinitialize(
fi_opt.set(
wind_directions=wd_array_opt,
wind_speeds=ws_array_opt,
turbulence_intensities=[0.08], # Assume 8% turbulence intensity
Expand Down
21 changes: 12 additions & 9 deletions examples/13_optimize_yaw_with_neighboring_farm.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def load_floris():
turbine_weights[0:10] = 1.0

# Now reinitialize FLORIS layout
fi.reinitialize(layout_x = X, layout_y = Y)
fi.set(layout_x = X, layout_y = Y)

# And visualize the floris layout
fig, ax = plt.subplots()
Expand Down Expand Up @@ -180,13 +180,13 @@ def yaw_opt_interpolant(wd, ws):

# Create a FLORIS object for AEP calculations
fi_AEP = fi.copy()
fi_AEP.reinitialize(wind_speeds=ws_windrose, wind_directions=wd_windrose)
fi_AEP.set(wind_speeds=ws_windrose, wind_directions=wd_windrose)

# And create a separate FLORIS object for optimization
fi_opt = fi.copy()
wd_array = np.arange(0.0, 360.0, 3.0)
ws_array = 8.0 * np.ones_like(wd_array)
fi_opt.reinitialize(
fi_opt.set(
wind_directions=wd_array,
wind_speeds=ws_array,
)
Expand Down Expand Up @@ -222,7 +222,7 @@ def yaw_opt_interpolant(wd, ws):

# Optimize yaw angles while ignoring neighboring farm
fi_opt_subset = fi_opt.copy()
fi_opt_subset.reinitialize(
fi_opt_subset.set(
layout_x = fi.layout_x[turbs_to_opt],
layout_y = fi.layout_y[turbs_to_opt]
)
Expand All @@ -239,15 +239,15 @@ def yaw_opt_interpolant(wd, ws):
print(" ")
print("===========================================================")
print("Calculating annual energy production with wake steering (AEP)...")
fi_AEP.set(yaw_angles=yaw_angles_opt_nonb_AEP)
aep_opt_subset_nonb = 1.0e-9 * fi_AEP.get_farm_AEP(
freq=freq_windrose,
turbine_weights=turbine_weights,
yaw_angles=yaw_angles_opt_nonb_AEP,
)
fi_AEP.set(yaw_angles=yaw_angles_opt_AEP)
aep_opt_subset = 1.0e-9 * fi_AEP.get_farm_AEP(
freq=freq_windrose,
turbine_weights=turbine_weights,
yaw_angles=yaw_angles_opt_AEP,
)
uplift_subset_nonb = 100.0 * (aep_opt_subset_nonb - aep_bl_subset) / aep_bl_subset
uplift_subset = 100.0 * (aep_opt_subset - aep_bl_subset) / aep_bl_subset
Expand All @@ -271,15 +271,18 @@ def yaw_opt_interpolant(wd, ws):
yaw_angles_opt_nonb[:, turbs_to_opt] = yaw_opt_interpolant_nonb(wd, ws)

fi_opt = fi_opt.copy()
fi_opt.calculate_wake(yaw_angles=np.zeros_like(yaw_angles_opt))
fi_opt.set(yaw_angles=np.zeros_like(yaw_angles_opt))
fi_opt.run()
farm_power_bl_subset = fi_opt.get_farm_power(turbine_weights).flatten()

fi_opt = fi_opt.copy()
fi_opt.calculate_wake(yaw_angles=yaw_angles_opt)
fi_opt.set(yaw_angles=yaw_angles_opt)
fi_opt.run()
farm_power_opt_subset = fi_opt.get_farm_power(turbine_weights).flatten()

fi_opt = fi_opt.copy()
fi_opt.calculate_wake(yaw_angles=yaw_angles_opt_nonb)
fi_opt.set(yaw_angles=yaw_angles_opt_nonb)
fi_opt.run()
farm_power_opt_subset_nonb = fi_opt.get_farm_power(turbine_weights).flatten()

fig, ax = plt.subplots()
Expand Down
5 changes: 3 additions & 2 deletions examples/14_compare_yaw_optimizers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
D = 126.0 # Rotor diameter for the NREL 5 MW
wd_array = np.arange(0.0, 360.0, 3.0)
ws_array = 8.0 * np.ones_like(wd_array)
fi.reinitialize(
fi.set(
layout_x=[0.0, 5 * D, 10 * D],
layout_y=[0.0, 0.0, 0.0],
wind_directions=wd_array,
Expand Down Expand Up @@ -92,7 +92,8 @@

# Before plotting results, need to compute values for GEOOPT since it doesn't compute
# power within the optimization
fi.calculate_wake(yaw_angles=yaw_angles_opt_geo)
fi.set(yaw_angles=yaw_angles_opt_geo)
fi.run()
geo_farm_power = fi.get_farm_power().squeeze()


Expand Down
Loading

0 comments on commit dc1b570

Please sign in to comment.