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

Eddy viscosity wake model #882

Draft
wants to merge 73 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
77945a6
Implement centerline deficit for single wake.
misi9170 Mar 14, 2024
f27805f
Compute offcenter velocities.
misi9170 Mar 14, 2024
0686bfb
Plot wake width.
misi9170 Mar 14, 2024
40bd8b5
Playing around a little with the filter function.
misi9170 Mar 14, 2024
fe3136a
Modeling expansion and combination model.
misi9170 Mar 21, 2024
4575bbb
Streamtube expansion and combination models running for aligned turbi…
misi9170 Mar 21, 2024
3b1c15a
Formatting.
misi9170 Mar 21, 2024
74d68d2
Format...
misi9170 Mar 21, 2024
ab46e30
Working in meandering.
misi9170 Mar 22, 2024
09a9b9e
Add temporary examples at top level to help with visualization.
misi9170 Mar 27, 2024
132da76
Merge branch 'v4' into feature/ev-model
misi9170 Mar 27, 2024
0a39fef
First pass of implementation in Floris form.
misi9170 Mar 27, 2024
80c7530
Remove incorrect factor of D.
misi9170 Mar 27, 2024
4dcab91
example updated.
misi9170 Mar 27, 2024
2bfa584
Plotting updates; fix upstream U_tilde.
misi9170 Mar 28, 2024
8f12369
WIP
misi9170 Mar 29, 2024
422f300
Working through ev solver implementation.
misi9170 Mar 29, 2024
53ddbe1
Close, but need combination model up and running.
misi9170 Mar 29, 2024
39de74e
Runs, seems approximately correct.
misi9170 Apr 3, 2024
0eed509
Masking to prevent warnings.
misi9170 Apr 3, 2024
279a8fa
Merge remote-tracking branch 'upstream/main' into feature/ev-model-fl…
misi9170 Apr 10, 2024
591b010
streamtube_expansion_solver runs after v4 merge.
misi9170 Apr 10, 2024
4110cd5
Support for different hub heights.
misi9170 Apr 10, 2024
8d8365a
Infrastrucutre for full flow streamtube expansion solver.
misi9170 Apr 10, 2024
d7975b1
full_flow_solver runs, but not correctly.
misi9170 Apr 11, 2024
b85c1c4
Fix incorrect input ordering.
misi9170 Apr 11, 2024
a6bb503
Support for sampling flow at points.
misi9170 Apr 11, 2024
daeeeb4
Isort
misi9170 Apr 11, 2024
b0be445
Clean up examples.
misi9170 Apr 11, 2024
5a1597e
Ruff, isort, cleanup.
misi9170 Apr 11, 2024
ccd3ed5
Skip ev examples in workflows.
misi9170 Apr 11, 2024
e1ad3ea
Examples run in folder; add parameters to input file.
misi9170 Apr 11, 2024
042e786
Ruff
misi9170 Apr 11, 2024
6fd148c
Isort, too...
misi9170 Apr 11, 2024
1936b13
Reg tests added, but failing small_grid_rotation currently.
misi9170 Apr 11, 2024
47043cb
Add handling for negative initial velocities and bad solve statuses; …
misi9170 Apr 15, 2024
ce43926
Update warning for negative velocities.
misi9170 Apr 15, 2024
a24eb67
Merge branch 'develop' into feature/ev-model
misi9170 Apr 15, 2024
7277e77
Remove commented out code; old implementations.
misi9170 Apr 15, 2024
b07a153
Bugfix in correcting wake_widths in streamtube expansion.
misi9170 Apr 16, 2024
92345a9
Remove temporary runscript.
misi9170 Apr 16, 2024
7da4413
Update reg tests, add symmetry test.
misi9170 Apr 16, 2024
16ce717
Merge branch 'develop' into feature/ev-model
misi9170 May 9, 2024
5ed14bc
Update EV model to accept turbulence_intensities input throughout.
misi9170 May 9, 2024
46043f4
Sort examples.
misi9170 Jun 26, 2024
e127b02
Replace demo notebook with script.
misi9170 Jun 26, 2024
98dd1f4
Remove redundant and old examples.
misi9170 Jun 26, 2024
41d9789
Attempted to reintroduce filter function, but not able to solve ODE.
misi9170 Jun 27, 2024
6583695
Ruff, isort.
misi9170 Jun 27, 2024
7a75a4d
Add brief documentation on EV.
misi9170 Jul 12, 2024
efd6b68
WIP; Gunn results.
misi9170 Jul 12, 2024
1e1c889
Merge develop
misi9170 Jul 15, 2024
695def3
Clean up 001 example
misi9170 Jul 15, 2024
c619407
Add paper links.
misi9170 Jul 15, 2024
efb5002
Fig 1. from Gunn
misi9170 Jul 15, 2024
f786fda
Minor aesthetic improvements to plot.
misi9170 Jul 15, 2024
f1965f2
Ruff, isort
misi9170 Jul 15, 2024
bdb2a71
Rename wd_std in eddy viscosity model for clarity.
misi9170 Jul 15, 2024
0d92c74
Bug, not using meandering version.
misi9170 Jul 15, 2024
44e0e03
Update 002 example.
misi9170 Jul 16, 2024
07481ed
Update reg tests after meandering bug. rtol on small_grid_rotation te…
misi9170 Jul 16, 2024
7a8552b
Improve figure labeling.
misi9170 Jul 16, 2024
197645c
Add example for wake visualization under wake steering.
misi9170 Jul 16, 2024
a1a681e
No support for wake deflections at this time.
misi9170 Jul 16, 2024
ca1b33f
Remove unneeded wake steering viz.
misi9170 Jul 16, 2024
d04a8dd
Add comment on SOED model to docs.
misi9170 Jul 16, 2024
7fe3ab7
Remove warnings caused by negative values in sqrt.
misi9170 Jul 16, 2024
7941219
Slightly increase minimum wake width to avoid numerical issues.
misi9170 Jul 16, 2024
ca01c9b
Add reference_wind_height to avoid warning.
misi9170 Jul 16, 2024
8590dc1
Combine figures.
misi9170 Jul 17, 2024
fab2162
Line length.
misi9170 Jul 17, 2024
5fe03b0
Rename eddy viscosity examples
misi9170 Jul 17, 2024
3c45a65
Merge develop
misi9170 Jul 18, 2024
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
26 changes: 26 additions & 0 deletions docs/references.bib
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,19 @@ @Article{bay_2022
DOI = {10.5194/wes-2022-17}
}

@article{gunn2019evmodel,
title = {Improvements to the Eddy Viscosity Wind Turbine Wake Model},
volume = {1222},
issn = {1742-6596},
url = {https://dx.doi.org/10.1088/1742-6596/1222/1/012003},
doi = {10.1088/1742-6596/1222/1/012003},
pages = {012003},
number = {1},
journaltitle = {Journal of Physics: Conference Series},
author = {Gunn, K.},
note = {Publisher: {IOP} Publishing},
}

@article{Pedersen_2022_turbopark2,
url = {https://dx.doi.org/10.1088/1742-6596/2265/2/022063},
year = {2022},
Expand All @@ -299,3 +312,16 @@ @article{SinnerFleming2024grs
title = {Robust wind farm layout optimization},
journal = {Journal of Physics: Conference Series},
}

@proceedings{Kuo2014soed,
author = {Kuo, Jim Y. J. and Romero, David A. and Amon, Cristina H.},
title = "{A Novel Wake Interaction Model for Wind Farm Layout Optimization}",
volume = {Volume 6B: Energy},
series = {ASME International Mechanical Engineering Congress and Exposition},
pages = {V06BT07A074},
year = {2014},
month = {11},
doi = {10.1115/IMECE2014-39073},
url = {https://doi.org/10.1115/IMECE2014-39073},
eprint = {https://asmedigitalcollection.asme.org/IMECE/proceedings-pdf/IMECE2014/46521/V06BT07A074/4267418/v06bt07a074-imece2014-39073.pdf},
}
29 changes: 29 additions & 0 deletions docs/wake_models.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,26 @@
"model_plot(\"../examples/inputs/turboparkgauss_cubature.yaml\", include_wake_deflection=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Eddy Viscosity\n",
"\n",
"The eddy viscosity model is a wake model originally presented by {cite:t}`ainslie1988calculating` and more recently advanced by {cite:t}`gunn2019evmodel`. The definition of the wake model requires solving an ordinary differential equation (ODE) to determine the wake centerline deficit at various downstream distances. Following {cite:t}`gunn2019evmodel` and others, the FLORIS implementation makes the self-similarity assumption to simplify the wake centerline ODE. The wake profile is Gaussian.\n",
"\n",
"Further, the wake width for individual turbine wakes is \"expanded\" upon encountering another turbine, and the eddy viscosity model is designed to be run with the \"sum of energy deficit\" (SOED) wake combination model."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model_plot(\"../examples/inputs/ev.yaml\", include_wake_deflection=False)"
]
},
{
"attachments": {},
"cell_type": "markdown",
Expand Down Expand Up @@ -360,6 +380,15 @@
"combination_plot(\"sosfs\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Sum of Energy Deficit (SOED)\n",
"\n",
"This model combines the wakes by conserving momentum, as described in {cite:t}`Kuo2014soed`."
]
},
{
"attachments": {},
"cell_type": "markdown",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
"""Example: Eddy viscosity model
This example demonstrates the wake profile of the eddy viscosity wake model,
presented originally by Ainslie (1988) and updated by Gunn (2019).
Links:
- Ainslie (1988): https://doi.org/10.1016/0167-6105(88)90037-2
- Gunn (2019): https://dx.doi.org/10.1088/1742-6596/1222/1/012003
"""

import matplotlib.pyplot as plt
import numpy as np

import floris.flow_visualization as flowviz
import floris.layout_visualization as layoutviz
from floris import FlorisModel


# Instantiate FLORIS model
fmodel = FlorisModel("../inputs/ev.yaml")

## Plot the flow velocity profiles
# Set up a two-turbine farm
D = 126
fmodel.set(layout_x=[0, 500, 1000], layout_y=[0, 0, 0])

fig, ax = plt.subplots(1, 1)
fig.set_size_inches(10, 4)
ax.scatter(fmodel.layout_x, fmodel.layout_y, color="red", label="Turbine location")

# Set a single wind condition of westerly wind
wd_array = np.array([270])
ws_array = 8.0 * np.ones_like(wd_array)
ti_array = 0.06 * np.ones_like(wd_array)
fmodel.set(wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=ti_array)

# Create points to sample the flow in the turbines' wakes, both at the centerline and
# across the wake's width
x_locs = np.linspace(0, 2000, 400)
y_locs = np.linspace(-100, 100, 41)
points_x, points_y = np.meshgrid(x_locs, y_locs)
points_x = points_x.flatten()
points_y = points_y.flatten()
points_z = 90 * np.ones_like(points_x)

# Collect the points
u_at_points = fmodel.sample_flow_at_points(points_x, points_y, points_z)
u_at_points = u_at_points.reshape((len(y_locs), len(x_locs), 1))

# Plot the flow velocities
for y_idx, y in enumerate(y_locs):
a = 1-np.abs(y/100)
if y_idx == (len(y_locs)-1)/2:
label = r"$r = 0D$ (centerline)"
elif y_idx == 3*(len(y_locs)-1)/4:
label = r"$r \approx 0.4D$"
elif y_idx == len(y_locs)-1:
label = r"$r \approx 0.8D$"
else:
label=None
ax.plot(x_locs, u_at_points[y_idx, :, 0].flatten(), color="black", alpha=a, label=label)
ax.grid()
ax.legend()
ax.set_xlabel("x location [m]")
ax.set_ylabel("Wind Speed [m/s]")

## Visualize the flow in aligned and slightly misaligned conditions for a 9-turbine farm
fig, ax = plt.subplots(2,2)
fig.set_size_inches(10, 10)
D = 126.0
x_locs = np.array([0, 5*D, 10*D])
y_locs = x_locs
points_x, points_y = np.meshgrid(x_locs, y_locs)
fmodel.set(
layout_x = points_x.flatten(),
layout_y = points_y.flatten()
)

# Aligned
layoutviz.plot_turbine_rotors(fmodel, ax=ax[0,0])
layoutviz.plot_turbine_labels(fmodel, ax=ax[0,0])

fmodel.set(
wind_speeds=[8.0, 8.0],
wind_directions=[270.0, 270.0+15.0],
turbulence_intensities=[0.06, 0.06]
)
fmodel.run()
cut_plane = fmodel.calculate_horizontal_plane(height=90, findex_for_viz=0)

flowviz.visualize_cut_plane(cut_plane, ax=ax[0,0])
ax[0,0].set_title("Aligned flow")

# Plot and print the power output of the turbines in each row
np.set_printoptions(formatter={"float": "{0:0.3f}".format})
print("Aligned case:")
for i in range(3):
idxs = [3*i, 3*i+1, 3*i+2]
ax[1,0].scatter([0,1,2], fmodel.get_turbine_powers()[0, idxs]/1e6, label="Column {0}".format(i))
print(idxs, " -- ", fmodel.get_turbine_powers()[0, idxs]/1e6)
ax[1,0].grid()
ax[1,0].legend()
ax[1,0].set_xlabel("Turbine in column")
ax[1,0].set_ylabel("Power [MW]")
ax[1,0].set_ylim([0.5, 1.8])

# Misaligned
layoutviz.plot_turbine_rotors(fmodel, yaw_angles=(-15.0)*np.ones(9), ax=ax[0,1])
layoutviz.plot_turbine_labels(fmodel, ax[0,1])

cut_plane = fmodel.calculate_horizontal_plane(height=90, findex_for_viz=1)
flowviz.visualize_cut_plane(cut_plane, ax=ax[0,1])
ax[0,1].set_title("Misaligned flow")

print("\nMisaligned case:")
for i in range(3):
idxs = [3*i, 3*i+1, 3*i+2]
ax[1,1].scatter([0,1,2], fmodel.get_turbine_powers()[1, idxs]/1e6, label="Column {0}".format(i))
print(idxs, " -- ", fmodel.get_turbine_powers()[1, idxs]/1e6)
ax[1,1].grid()
ax[1,1].legend()
ax[1,1].set_xlabel("Turbine in column")
ax[1,1].set_ylabel("Power [MW]")
ax[1,1].set_ylim([0.5, 1.8])

plt.show()
153 changes: 153 additions & 0 deletions examples/examples_eddy_viscosity/002_reproduce_published_results.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"""Example: Reproduce published eddy viscosity results
This example attempts to reproduce the results of Ainslie (1988) and Gunn (2019)
using the FLORIS implementation of the eddy viscosity model.

Links:
- Ainslie (1988): https://doi.org/10.1016/0167-6105(88)90037-2
- Gunn (2019): https://dx.doi.org/10.1088/1742-6596/1222/1/012003
"""

import matplotlib.pyplot as plt
import numpy as np

from floris import FlorisModel
from floris.turbine_library import build_cosine_loss_turbine_dict


# Build a constant CT turbine model for use in comparisons (not realistic)
u_0 = 8.0 # wind speed [m/s]

# Load the EV model
fmodel = FlorisModel("../inputs/ev.yaml")

## First, attempt to reproduce Figs. 3 and 4 from Ainslie (1988).

# It is not clear exactly how the parametrization of Gunn, which is the one
# that is implemented in the EddyViscosityVelocity model, can be matched to
# the parameters given by Ainslie. Rather than try to do this, we simply
# generate plots similar to Figs. 3 and 4. using the default parameters following
# Gunn (2019).

# Generate figure to plot on
fig, ax = plt.subplots(1, 2, sharex=True, sharey=True)
fig.set_size_inches(8, 4)

HH = 50
D = 50

for i, wd_std_ev in enumerate([0.0, np.rad2deg(0.1)]):
fmodel.set_param(["wake", "wake_velocity_parameters", "eddy_viscosity", "wd_std_ev"], wd_std_ev)
for C_T, ls in zip([0.5, 0.7, 0.9], ["-", "--", ":"]):
const_CT_turb = build_cosine_loss_turbine_dict(
turbine_data_dict={
"wind_speed":[0.0, 30.0],
"power":[0.0, 1.0], # Not realistic but won't be used here
"thrust_coefficient":[C_T, C_T]
},
turbine_name="ConstantCT",
rotor_diameter=D,
hub_height=HH,
ref_tilt=0.0,
)

# Load the EV model and set a constant CT turbine
fmodel.set(
layout_x=[0],
layout_y=[0],
turbine_type=[const_CT_turb],
wind_speeds=[u_0],
wind_directions=[270],
turbulence_intensities=[0.14], # As specified by Ainslie
wind_shear=0.0,
reference_wind_height=HH
)

points_x = np.linspace(2*D, 10*D, 1000)
points_y = np.zeros_like(points_x)
points_z = HH * np.ones_like(points_x)

u_at_points = fmodel.sample_flow_at_points(points_x, points_y, points_z)

# Plot results (not different axis scales in Ainslie)
ax[i].plot(
points_x/D, 1-u_at_points[0, :]/u_0, color="k", linestyle=ls, label=rf"$C_T$ = {C_T}"
)
ax[i].set_title(r"WD std. dev. $\sigma_{\theta} = $"+"{:.1f} deg".format(wd_std_ev))

ax[0].set_xlabel("Downstream distance [D]")
ax[1].set_xlabel("Downstream distance [D]")
ax[0].set_ylabel("Centerline velocity deficit [-]")
ax[0].set_ylim([0, 1])
ax[0].legend()
ax[0].grid()
ax[1].legend()
ax[1].grid()


## Second, reproduce Figure 1b (right) from Gunn (2019)

# Reset to the default model parameters
fmodel = FlorisModel("../inputs/ev.yaml")

# Adjustments
C_T2 = 0.34
D2 = 1.52

y_offset = -20
HH = 0.8

# Match depends on ambient turbulence intensity. 7.5% appears close. Also, switch off meandering.
TI = 0.075
fmodel.set_param(["wake", "wake_velocity_parameters", "eddy_viscosity", "wd_std_ev"], 0.0)

turb_1 = build_cosine_loss_turbine_dict(
turbine_data_dict={
"wind_speed":[0.0, 30.0],
"power":[0.0, 1.0], # Not realistic but won't be used here
"thrust_coefficient":[0.8, 0.8]
},
turbine_name="ConstantCT1",
rotor_diameter=1,
hub_height=HH,
ref_tilt=0.0,
)
turb_2 = build_cosine_loss_turbine_dict(
turbine_data_dict={
"wind_speed":[0.0, 30.0],
"power":[0.0, 1.0], # Not realistic but won't be used here
"thrust_coefficient":[C_T2, C_T2]
},
turbine_name="ConstantCT2",
rotor_diameter=D2,
hub_height=HH,
ref_tilt=0.0,
)

fmodel.set(
layout_x=[0, 1.5],
layout_y=[0, y_offset],
turbine_type=[turb_1, turb_2],
wind_speeds=[u_0],
wind_directions=[270.0],
turbulence_intensities=[TI],
wind_shear=0.0,
reference_wind_height=HH
)

n_pts = 1000
points_x = np.concatenate((np.linspace(0, 10, n_pts), np.linspace(1.5, 11.5, n_pts)))
points_y = np.concatenate((np.zeros(n_pts), y_offset*np.ones(n_pts)))
points_z = HH * np.ones_like(points_x)
u_at_points = fmodel.sample_flow_at_points(points_x, points_y, points_z)

fig, ax = plt.subplots(1,1)
ax.plot([0.0, 0.0], [0.3, 1.1], color="black")
ax.plot([1.5, 1.5], [0.3, 1.1], color="black")
ax.plot(points_x[:n_pts], u_at_points[0, :n_pts]/u_0, color="C0")
ax.plot(points_x[n_pts:], u_at_points[0, n_pts:]/u_0, color="C2", linestyle="--")
ax.grid()
ax.set_xlabel(r"$X$")
ax.set_ylabel(r"$\tilde{U}_c$")
ax.set_ylim([0.3, 1.1])

plt.show()
Loading
Loading