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

Add wake deflection to the TurbOPark wake deficit model #439

Merged
merged 6 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
42 changes: 33 additions & 9 deletions floris/simulation/solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,7 @@ def turbopark_solver(farm: Farm, flow_field: FlowField, grid: TurbineGrid, model
w_wake = np.zeros_like(flow_field.w_initial_sorted)
shape = (farm.n_turbines,) + np.shape(flow_field.u_initial_sorted)
velocity_deficit = np.zeros(shape)
deflection_field = np.zeros_like(flow_field.u_initial_sorted)

turbine_turbulence_intensity = flow_field.turbulence_intensity * np.ones((flow_field.n_wind_directions, flow_field.n_wind_speeds, farm.n_turbines, 1, 1))
ambient_turbulence_intensity = flow_field.turbulence_intensity
Expand Down Expand Up @@ -769,15 +770,37 @@ def turbopark_solver(farm: Farm, flow_field: FlowField, grid: TurbineGrid, model

# Model calculations
# NOTE: exponential
deflection_field = model_manager.deflection_model.function(
x_i,
y_i,
effective_yaw_i,
turbulence_intensity_i,
ct_i,
rotor_diameter_i,
**deflection_model_args
)
if not np.all(farm.yaw_angles_sorted):
model_manager.deflection_model.logger.warning("WARNING: Deflection with the TurbOPark model has not been fully validated. This is an initial implementation, and we advise you use at your own risk and perform a thorough examination of the results.")
for ii in range(i):
x_ii = np.mean(grid.x_sorted[:, :, ii:ii+1], axis=(3, 4))
x_ii = x_ii[:, :, :, None, None]
y_ii = np.mean(grid.y_sorted[:, :, ii:ii+1], axis=(3, 4))
y_ii = y_ii[:, :, :, None, None]

yaw_ii = farm.yaw_angles_sorted[:, :, ii:ii+1, None, None]
turbulence_intensity_ii = turbine_turbulence_intensity[:, :, ii:ii+1]
ct_ii = Ct(
velocities=flow_field.u_sorted,
yaw_angle=farm.yaw_angles_sorted,
fCt=farm.turbine_fCts,
turbine_type_map=farm.turbine_type_map_sorted,
ix_filter=[ii]
)
ct_ii = ct_ii[:, :, 0:1, None, None]
rotor_diameter_ii = farm.rotor_diameters_sorted[: ,:, ii:ii+1, None, None]

deflection_field_ii = model_manager.deflection_model.function(
x_ii,
y_ii,
yaw_ii,
turbulence_intensity_ii,
ct_ii,
rotor_diameter_ii,
**deflection_model_args
)

deflection_field[:,:,ii:ii+1,:,:] = deflection_field_ii[:,:,i:i+1,:,:]

if model_manager.enable_transverse_velocities:
v_wake, w_wake = calculate_transverse_velocity(
Expand Down Expand Up @@ -816,6 +839,7 @@ def turbopark_solver(farm: Farm, flow_field: FlowField, grid: TurbineGrid, model
rotor_diameter_i,
farm.rotor_diameters_sorted[:, :, :, None, None],
i,
deflection_field,
**deficit_model_args
)

Expand Down
5 changes: 3 additions & 2 deletions floris/simulation/wake_velocity/turbopark.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def function(
rotor_diameter_i: np.ndarray,
rotor_diameters: np.ndarray,
i: int,
deflection_field: np.ndarray,
# enforces the use of the below as keyword arguments and adherence to the
# unpacking of the results from prepare_function()
*,
Expand All @@ -89,8 +90,8 @@ def function(
x_dist = (x_i - x) * downstream_mask / rotor_diameters

# Radial distance between turbine i and the centerlines of wakes from all real/image turbines
r_dist = np.sqrt((y_i - y) ** 2 + (z_i - z) ** 2)
r_dist_image = np.sqrt((y_i - y) ** 2 + (z_i - (-z)) ** 2)
r_dist = np.sqrt((y_i - (y + deflection_field)) ** 2 + (z_i - z) ** 2)
r_dist_image = np.sqrt((y_i - (y + deflection_field)) ** 2 + (z_i - (-z)) ** 2)

Cts[:,:,i:,:,:] = 0.00001

Expand Down
14 changes: 2 additions & 12 deletions floris/tools/floris_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,7 @@ def calculate_wake(
# )

# TODO decide where to handle this sign issue
if (yaw_angles is not None) and not (np.all(yaw_angles == 0.0)):
if self.floris.wake.model_strings["velocity_model"] == "turbopark":
# TODO: Implement wake steering for the TurbOPark model
raise ValueError(
"Non-zero yaw angles given and for TurbOPark model; wake steering with this model is not yet implemented."
)
if (yaw_angles is not None) and not (np.all(yaw_angles==0.)):
self.floris.farm.yaw_angles = yaw_angles

# Initialize solution space
Expand All @@ -149,12 +144,7 @@ def calculate_no_wake(
"""

# TODO decide where to handle this sign issue
if (yaw_angles is not None) and not (np.all(yaw_angles == 0.0)):
if self.floris.wake.model_strings["velocity_model"] == "turbopark":
# TODO: Implement wake steering for the TurbOPark model
raise ValueError(
"Non-zero yaw angles given and for TurbOPark model; wake steering with this model is not yet implemented."
)
if (yaw_angles is not None) and not (np.all(yaw_angles==0.)):
self.floris.farm.yaw_angles = yaw_angles

# Initialize solution space
Expand Down
2 changes: 1 addition & 1 deletion tests/reg_tests/cumulative_curl_regression_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from floris.simulation import Ct, power, axial_induction, average_velocity
from tests.conftest import N_TURBINES, N_WIND_DIRECTIONS, N_WIND_SPEEDS, print_test_values, assert_results_arrays

DEBUG = True
DEBUG = False
VELOCITY_MODEL = "cc"
DEFLECTION_MODEL = "gauss"

Expand Down
97 changes: 96 additions & 1 deletion tests/reg_tests/turbopark_regression_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from floris.simulation import Ct, power, axial_induction, average_velocity
from tests.conftest import N_TURBINES, N_WIND_DIRECTIONS, N_WIND_SPEEDS, print_test_values, assert_results_arrays

DEBUG = True
DEBUG = False
VELOCITY_MODEL = "turbopark"
DEFLECTION_MODEL = "gauss"
COMBINATION_MODEL = "fls"
Expand Down Expand Up @@ -53,6 +53,35 @@
)


yawed_baseline = np.array(
[
# 8 m/s
[
[7.9803783, 0.7605249, 1683956.5765064, 0.2548147],
[5.9926862, 0.8357973, 704869.1763857, 0.2973903],
[5.3145419, 0.8725432, 479691.1339821, 0.3214945],
],
# 9 m/s
[
[8.9779256, 0.7596713, 2397236.5542849, 0.2543815],
[6.7429885, 0.8025994, 1023094.6963579, 0.2778511],
[5.9836502, 0.8362597, 701734.6626599, 0.2976758],
],
# 10 m/s
[
[9.9754729, 0.7499157, 3283591.8023665, 0.2494847],
[7.5085974, 0.7756254, 1412651.4697014, 0.2631590],
[6.6781823, 0.8052071, 992930.8979929, 0.2793232],
],
# 11 m/s
[
[10.9730201, 0.7276532, 4344222.0129382, 0.2386508],
[8.3071319, 0.7620861, 1916104.8725891, 0.2561179],
[7.3875052, 0.7795398, 1347926.7384587, 0.2652341],
],
]
)

# Note: compare the yawed vs non-yawed results. The upstream turbine
# power should be lower in the yawed case. The following turbine
# powers should higher in the yawed case.
Expand Down Expand Up @@ -199,6 +228,72 @@ def test_regression_rotation(sample_inputs_fixture):
assert np.allclose(t3_270, t2_360)


def test_regression_yaw(sample_inputs_fixture):
"""
Tandem turbines with the upstream turbine yawed
"""
sample_inputs_fixture.floris["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL
sample_inputs_fixture.floris["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL

floris = Floris.from_dict(sample_inputs_fixture.floris)

yaw_angles = np.zeros((N_WIND_DIRECTIONS, N_WIND_SPEEDS, N_TURBINES))
yaw_angles[:,:,0] = 5.0
floris.farm.yaw_angles = yaw_angles

floris.initialize_domain()
floris.steady_state_atmospheric_condition()

n_turbines = floris.farm.n_turbines
n_wind_speeds = floris.flow_field.n_wind_speeds
n_wind_directions = floris.flow_field.n_wind_directions

velocities = floris.flow_field.u
yaw_angles = floris.farm.yaw_angles
test_results = np.zeros((n_wind_directions, n_wind_speeds, n_turbines, 4))

farm_avg_velocities = average_velocity(
velocities,
)
farm_cts = Ct(
velocities,
yaw_angles,
floris.farm.turbine_fCts,
floris.farm.turbine_type_map,
)
farm_powers = power(
floris.flow_field.air_density,
velocities,
yaw_angles,
floris.farm.pPs,
floris.farm.turbine_power_interps,
floris.farm.turbine_type_map,
)
farm_axial_inductions = axial_induction(
velocities,
yaw_angles,
floris.farm.turbine_fCts,
floris.farm.turbine_type_map,
)
for i in range(n_wind_directions):
for j in range(n_wind_speeds):
for k in range(n_turbines):
test_results[i, j, k, 0] = farm_avg_velocities[i, j, k]
test_results[i, j, k, 1] = farm_cts[i, j, k]
test_results[i, j, k, 2] = farm_powers[i, j, k]
test_results[i, j, k, 3] = farm_axial_inductions[i, j, k]

if DEBUG:
print_test_values(
farm_avg_velocities,
farm_cts,
farm_powers,
farm_axial_inductions,
)

assert_results_arrays(test_results[0], yawed_baseline)


def test_regression_small_grid_rotation(sample_inputs_fixture):
"""
Where wake models are masked based on the x-location of a turbine, numerical precision
Expand Down