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

SciPy Yaw optimization classes: improved efficiency, automated downstream turbine exclusion and turbine-dependent weighing terms #245

Merged
merged 28 commits into from
Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2d1a873
Implement yaw optimization functionality to automatically exclude tur…
Jul 22, 2021
5fb31cb
Add turbine weighing option to yaw.py in the scipy optimization suite…
Jul 22, 2021
35455a8
Implement functionality in YawOptimizationWindRose to only optimize f…
Jul 22, 2021
41691b9
Add functionality to yaw.py to automatically estimate what turbines a…
Jul 22, 2021
ae75697
Implement similar functionality for exclusion of downstream turbines …
Jul 22, 2021
108e20f
Quick bug fix for new features in yaw.py and yaw_wind_rose.py. Array …
Jul 22, 2021
fc85499
Implement new features in parallelized yaw optimization function too.…
Jul 22, 2021
c0775ce
Refactor some of the recent improvements into a separate function. Al…
Jul 22, 2021
63183a4
Bug fix for setting bounds closest to 0. when non-zero bounds are spe…
Aug 10, 2021
7dbaa24
Further fix bound fixing for downstream turbines. Also, add descripti…
Aug 10, 2021
6504d50
Add description for exclude_downstream_turbines docstring
Aug 10, 2021
55d39c4
Bug fixes to address applying bounds to a downstream machine when exc…
Aug 10, 2021
64c00b6
Move _reduce_control_variables to private methods
Aug 10, 2021
58ca7af
Add self.x_baseline as being the yaw angles that are inherently insid…
Aug 18, 2021
0bedbc3
Add more elaborate description in simple wake model and set a higher …
Aug 19, 2021
b65e0d2
Implement yaw_angles_baseline as separate variable
Aug 19, 2021
b40097b
Add INFO statement if not all baseline yaw angles are zero and also i…
Aug 19, 2021
ffd61f8
Add a variable called self.yaw_angles_template where the yaw angles a…
Aug 20, 2021
6ecf10a
Update yaw_angles_template to have a non-nan value for every turbine …
Aug 20, 2021
401e53e
Remove unused variables in _reduce_control_variables
Aug 20, 2021
954b336
Implement recent changes in yaw.py to yaw_wind_rose.py and yaw_wind_r…
Aug 20, 2021
6ed1d13
changing imports to be relative
bayc Aug 24, 2021
cddfe91
fixing power_rose plot; changing how string is created
bayc Aug 24, 2021
0e0bf58
Merge remote-tracking branch 'origin/develop' into revised-optimization
bayc Aug 24, 2021
e76fc4b
updating formatting
bayc Aug 24, 2021
c87dc35
Update yaw.py docstring
Aug 27, 2021
8bfa4e9
Update yaw_wind_rose.py and _parallel.py docstring
Aug 27, 2021
b83b67b
Merge branch 'revised-optimization' of github.com:Bartdoekemeijer/flo…
Aug 27, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
# Plot and show
fig, ax = plt.subplots()
wfct.visualization.visualize_cut_plane(hor_plane, ax=ax)
ax.set_title(r"Baseline flow for U = 8 m/s, Wind Direction = 270$^\\circ$")
ax.set_title("Baseline flow for U = 8 m/s, Wind Direction = 270$^\\circ$")

# ==============================================================================
print("Importing wind rose data...")
Expand Down
1 change: 1 addition & 0 deletions floris/tools/optimization/scipy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@
yaw_wind_rose,
power_density_1D,
yaw_wind_rose_parallel,
derive_downstream_turbines,
)
143 changes: 143 additions & 0 deletions floris/tools/optimization/scipy/derive_downstream_turbines.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# Copyright 2021 NREL

# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.

# See https://floris.readthedocs.io for documentation


import numpy as np
import matplotlib.pyplot as plt


def derive_downstream_turbines(fi, wind_direction, wake_slope=0.30, plot_lines=False):
"""Determine which turbines have no effect on other turbines in the
farm, i.e., which turbines have wakes that do not impact the other
turbines in the farm. This allows the user to exclude these turbines
from a control setpoint optimization, for example. This function
assumes a very simplified wake function where the wakes are assumed
to have a linearly diverging profile. In comparisons with the FLORIS
GCH model, the wake_slope matches well with the FLORIS' wake profiles
for a value of wake_slope = 0.5 * turbulence_intensity, where
turbulence_intensity is an input to the FLORIS model at the default
GCH parameterization. Note that does not include wind direction variability.
To be conservative, the user is recommended to use the rule of thumb:
`wake_slope = turbulence_intensity`. Hence, the default value for
`wake_slope=0.30` should be conservative for turbulence intensities up to
0.30 and is likely to provide valid estimates of which turbines are
downstream until a turbulence intensity of 0.50. This simple model saves
time compared to FLORIS.

Args:
fi ([floris object]): FLORIS object of the farm of interest.
wind_direction (float): The wind direction in the FLORIS frame
of reference for which the downstream turbines are to be determined.
wake_slope (float, optional): linear slope of the wake (dy/dx)
plot_lines (bool, optional): Enable plotting wakes/turbines.
Defaults to False.

Returns:
turbs_downstream (iterable): A list containing the turbine
numbers that have a wake that does not affect any other
turbine inside the farm.
"""

# Get farm layout
x = fi.layout_x
y = fi.layout_y
D = np.array([t.rotor_diameter for t in fi.floris.farm.turbines])
n_turbs = len(x)

# Rotate farm and determine freestream/waked turbines
is_downstream = [False for _ in range(n_turbs)]
x_rot = (
np.cos((wind_direction - 270.0) * np.pi / 180.0) * x
- np.sin((wind_direction - 270.0) * np.pi / 180.0) * y
)
y_rot = (
np.sin((wind_direction - 270.0) * np.pi / 180.0) * x
+ np.cos((wind_direction - 270.0) * np.pi / 180.0) * y
)

if plot_lines:
fig, ax = plt.subplots()
for ii in range(n_turbs):
ax.plot(
x_rot[ii] * np.ones(2),
[y_rot[ii] - D[ii] / 2, y_rot[ii] + D[ii] / 2],
"k",
)
for ii in range(n_turbs):
ax.text(x_rot[ii], y_rot[ii], "T%03d" % ii)
ax.axis("equal")

srt = np.argsort(x_rot)
x_rot_srt = x_rot[srt]
y_rot_srt = y_rot[srt]
for ii in range(n_turbs):
x0 = x_rot_srt[ii]
y0 = y_rot_srt[ii]

def wake_profile_ub_turbii(x):
y = (y0 + D[ii]) + (x - x0) * wake_slope
if isinstance(y, (float, np.float64, np.float32)):
if x < (x0 + 0.01):
y = -np.Inf
else:
y[x < x0 + 0.01] = -np.Inf
return y

def wake_profile_lb_turbii(x):
y = (y0 - D[ii]) - (x - x0) * wake_slope
if isinstance(y, (float, np.float64, np.float32)):
if x < (x0 + 0.01):
y = -np.Inf
else:
y[x < x0 + 0.01] = -np.Inf
return y

def determine_if_in_wake(xt, yt):
return (yt < wake_profile_ub_turbii(xt)) & (yt > wake_profile_lb_turbii(xt))

is_downstream[ii] = not any(
[
determine_if_in_wake(x_rot_srt[iii], y_rot_srt[iii])
for iii in range(n_turbs)
]
)

if plot_lines:
x1 = np.max(x_rot_srt) + 500.0
ax.fill_between(
[x0, x1, x1, x0],
[
wake_profile_ub_turbii(x0 + 0.02),
wake_profile_ub_turbii(x1),
wake_profile_lb_turbii(x1),
wake_profile_lb_turbii(x0 + 0.02),
],
alpha=0.1,
color="k",
edgecolor=None,
)

usrt = np.argsort(srt)
is_downstream = [is_downstream[i] for i in usrt]
turbs_downstream = list(np.where(is_downstream)[0])

if plot_lines:
ax.set_title("wind_direction = %03d" % wind_direction)
ax.set_xlim([np.min(x_rot) - 500.0, x1])
ax.set_ylim([np.min(y_rot) - 500.0, np.max(y_rot) + 500.0])
ax.plot(
x_rot[turbs_downstream], y_rot[turbs_downstream], "o", color="green",
)

return turbs_downstream
Loading