Skip to content
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
10 changes: 7 additions & 3 deletions docs/operation_models_user.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,14 @@
"The `\"cosine-loss\"` operation model describes the decrease in power and thrust produced by a \n",
"wind turbine as it yaws (or tilts) away from the incoming wind. The thrust is reduced by a factor of \n",
"$\\cos \\gamma$, where $\\gamma$ is the yaw misalignment angle, while the power is reduced by a factor \n",
"of $(\\cos\\gamma)^{p_P}$, where $p_P$ is the cosine loss exponent, specified by `cosine_loss_exponent_yaw`\n",
"(or `cosine_loss_exponent_tilt` for tilt angles). The power and thrust produced by the turbine\n",
"of $(\\cos\\gamma)^{p_P}$, where $p_P$ is the cosine loss exponent, specified by `cosine_loss_exponent_yaw`.\n",
"Similarly, the thrust and power are reduced by factors of $(\\cos \\theta / \\cos \\theta_{ref})$ and\n",
"$(\\cos \\theta / \\cos \\theta_{ref})^{p_T}$, respectively, where $\\theta$ is the tilt angle, \n",
"$\\theta_{ref}$ is the reference tilt angle under which the power and thrust curves are specified, and\n",
"$p_T$ is the cosine loss exponent for tilt, specified by `cosine_loss_exponent_tilt`.\n",
"The power and thrust produced by the turbine\n",
"thus vary as a function of the turbine's yaw angle, set using the `yaw_angles` argument to \n",
"`FlorisModel.set()`."
"`FlorisModel.set()`, and tilt angle (if applicable, for example for floating wind turbines)."
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions floris/core/rotor_velocity.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ def rotor_velocity_tilt_cosine_correction(
tilt_angles = np.where(correct_cp_ct_for_tilt, tilt_angles, old_tilt_angle)

# Compute the rotor effective velocity adjusting for tilt
relative_tilt = tilt_angles - ref_tilt
rotor_effective_velocities = (
rotor_effective_velocities
* cosd(relative_tilt) ** (cosine_loss_exponent_tilt / 3.0)
* (cosd(tilt_angles) / cosd(ref_tilt)) ** (cosine_loss_exponent_tilt / 3.0)
)

return rotor_effective_velocities

def simple_mean(array, axis=0):
Expand Down
7 changes: 5 additions & 2 deletions floris/core/turbine/operation_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,8 @@ def thrust_coefficient(
thrust_coefficient = (
thrust_coefficient
* cosd(yaw_angles)
* cosd(tilt_angles - power_thrust_table["ref_tilt"])
* cosd(tilt_angles)
/ cosd(power_thrust_table["ref_tilt"])
)

return thrust_coefficient
Expand Down Expand Up @@ -294,7 +295,9 @@ def axial_induction(
correct_cp_ct_for_tilt=correct_cp_ct_for_tilt
)

misalignment_loss = cosd(yaw_angles) * cosd(tilt_angles - power_thrust_table["ref_tilt"])
misalignment_loss = (
cosd(yaw_angles) * cosd(tilt_angles) / cosd(power_thrust_table["ref_tilt"])
)
return 0.5 / misalignment_loss * (1 - np.sqrt(1 - thrust_coefficient * misalignment_loss))

@define
Expand Down
127 changes: 127 additions & 0 deletions tests/reg_tests/empirical_gauss_regression_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,36 @@
]
)

tilted_baseline = np.array(
[
# 8 m/s
[
[7.9736858, 0.7824685, 1734488.7059275, 0.2658985],
[5.8875889, 0.8705526, 704778.1875928, 0.3212047],
[5.9110415, 0.8692142, 712622.7895048, 0.3202651],
],
# 9 m/s
[
[8.9703965, 0.7812020, 2471404.7824861, 0.2652278],
[6.6276158, 0.8354859, 1026885.3722728, 0.2980366],
[6.7091646, 0.8317630, 1063636.4762428, 0.2957326],
],
# 10 m/s
[
[9.9671073, 0.7792154, 3383206.6144193, 0.2641794],
[7.3711242, 0.8050505, 1396968.1317078, 0.2799135],
[7.5109456, 0.8003819, 1477742.2826442, 0.2772650],
],
# 11 m/s
[
[10.9638180, 0.7520150, 4470666.5322783, 0.2502624],
[8.2150645, 0.7898565, 1946589.2518193, 0.2714076],
[8.3045772, 0.7897407, 2013649.9637290, 0.2713440],
]
]
)


yaw_added_recovery_baseline = np.array(
[
# 8 m/s
Expand Down Expand Up @@ -453,6 +483,103 @@ def test_regression_yaw(sample_inputs_fixture):

assert_results_arrays(test_results[0:4], yawed_baseline)


def test_regression_tilt(sample_inputs_fixture):
"""
Tandem turbines with the upstream turbine tilted
"""
sample_inputs_fixture.core["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL
sample_inputs_fixture.core["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL
sample_inputs_fixture.core["wake"]["model_strings"]["turbulence_model"] = TURBULENCE_MODEL

floris = Core.from_dict(sample_inputs_fixture.core)

tilt_angles = np.zeros((N_FINDEX, N_TURBINES))
tilt_angles[:,0] = 8.0
floris.farm.tilt_angles = tilt_angles

floris.initialize_domain()
floris.steady_state_atmospheric_condition()

n_turbines = floris.farm.n_turbines
n_findex = floris.flow_field.n_findex

velocities = floris.flow_field.u
turbulence_intensities = floris.flow_field.turbulence_intensity_field
air_density = floris.flow_field.air_density
yaw_angles = floris.farm.yaw_angles
tilt_angles = floris.farm.tilt_angles
power_setpoints = floris.farm.power_setpoints
awc_modes = floris.farm.awc_modes
awc_amplitudes = floris.farm.awc_amplitudes
test_results = np.zeros((n_findex, n_turbines, 4))

farm_avg_velocities = average_velocity(
velocities,
)
farm_cts = thrust_coefficient(
velocities,
turbulence_intensities,
air_density,
yaw_angles,
tilt_angles,
power_setpoints,
awc_modes,
awc_amplitudes,
floris.farm.turbine_thrust_coefficient_functions,
floris.farm.turbine_tilt_interps,
floris.farm.correct_cp_ct_for_tilt,
floris.farm.turbine_type_map,
floris.farm.turbine_power_thrust_tables,
)
farm_powers = power(
velocities,
turbulence_intensities,
air_density,
floris.farm.turbine_power_functions,
yaw_angles,
tilt_angles,
power_setpoints,
awc_modes,
awc_amplitudes,
floris.farm.turbine_tilt_interps,
floris.farm.turbine_type_map,
floris.farm.turbine_power_thrust_tables,
)
farm_axial_inductions = axial_induction(
velocities,
turbulence_intensities,
air_density,
yaw_angles,
tilt_angles,
power_setpoints,
awc_modes,
awc_amplitudes,
floris.farm.turbine_axial_induction_functions,
floris.farm.turbine_tilt_interps,
floris.farm.correct_cp_ct_for_tilt,
floris.farm.turbine_type_map,
floris.farm.turbine_power_thrust_tables,
)
for i in range(n_findex):
for j in range(n_turbines):
test_results[i, j, 0] = farm_avg_velocities[i, j]
test_results[i, j, 1] = farm_cts[i, j]
test_results[i, j, 2] = farm_powers[i, j]
test_results[i, j, 3] = farm_axial_inductions[i, j]

if DEBUG:
print_test_values(
farm_avg_velocities,
farm_cts,
farm_powers,
farm_axial_inductions,
max_findex_print=4,
)

assert_results_arrays(test_results[0:4], tilted_baseline)


def test_regression_yaw_added_recovery(sample_inputs_fixture):
"""
Tandem turbines with the upstream turbine yawed and yaw added recovery
Expand Down
54 changes: 54 additions & 0 deletions tests/rotor_velocity_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,60 @@ def test_rotor_velocity_tilt_cosine_correction():

np.testing.assert_allclose(tilt_corrected_velocities, wind_speed_N_TURBINES)

## Test angles that are not the same as the reference tilt

# Test tilted "back" from reference tilt of 5 degrees
tilt_angle = 10.0 # Greater than the reference tilt
tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction(
tilt_angles=tilt_angle*np.ones((1, N_TURBINES)),
ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES),
cosine_loss_exponent_tilt=np.array(
[turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES
),
tilt_interp=None, # Override wind-speed-based tilt interpolation
correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]),
rotor_effective_velocities=wind_speed_N_TURBINES,
)
assert (tilt_corrected_velocities < wind_speed_N_TURBINES).all()

# Test tilted "forward" from reference tilt of 5 degrees
tilt_angle = 0.0 # Less than the reference tilt
tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction(
tilt_angles=tilt_angle*np.ones((1, N_TURBINES)),
ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES),
cosine_loss_exponent_tilt=np.array(
[turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES
),
tilt_interp=None, # Override wind-speed-based tilt interpolation
correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]),
rotor_effective_velocities=wind_speed_N_TURBINES,
)
assert (tilt_corrected_velocities > wind_speed_N_TURBINES).all()

# Test symmetry around zero tilt
tilt_angle = 3.0
tilt_negative = rotor_velocity_tilt_cosine_correction(
tilt_angles=-tilt_angle*np.ones((1, N_TURBINES)),
ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES),
cosine_loss_exponent_tilt=np.array(
[turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES
),
tilt_interp=None, # Override wind-speed-based tilt interpolation
correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]),
rotor_effective_velocities=wind_speed_N_TURBINES,
)
tilt_positive = rotor_velocity_tilt_cosine_correction(
tilt_angles=tilt_angle*np.ones((1, N_TURBINES)),
ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES),
cosine_loss_exponent_tilt=np.array(
[turbine_floating.power_thrust_table["cosine_loss_exponent_tilt"]] * N_TURBINES
),
tilt_interp=None, # Override wind-speed-based tilt interpolation
correct_cp_ct_for_tilt=np.array([[True] * N_TURBINES]),
rotor_effective_velocities=wind_speed_N_TURBINES,
)
np.testing.assert_allclose(tilt_negative, tilt_positive)

def test_compute_tilt_angles_for_floating_turbines():
N_TURBINES = 4

Expand Down
45 changes: 22 additions & 23 deletions tests/turbine_multi_dim_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def test_ct():
multidim_condition=condition
)

np.testing.assert_allclose(thrust, np.array([[0.77815736]]))
np.testing.assert_allclose(thrust, np.array([[0.77958497]]))

# Multiple turbines with index filter
# 4 turbines with 3 x 3 grid arrays
Expand All @@ -123,27 +123,26 @@ def test_ct():
)
assert len(thrusts[0]) == len(INDEX_FILTER)

thrusts_truth = np.array([
[0.77815736, 0.77815736],
[0.77815736, 0.77815736],
[0.77815736, 0.77815736],
[0.66626835, 0.66626835 ],

[0.77815736, 0.77815736],
[0.77815736, 0.77815736],
[0.77815736, 0.77815736],
[0.66626835, 0.66626835 ],

[0.77815736, 0.77815736],
[0.77815736, 0.77815736],
[0.77815736, 0.77815736],
[0.66626835, 0.66626835 ],

[0.77815736, 0.77815736],
[0.77815736, 0.77815736],
[0.77815736, 0.77815736],
[0.66626835, 0.66626835 ],
])
thrusts_truth = np.array(
[
[0.77958497, 0.77958497],
[0.77958497, 0.77958497],
[0.77958497, 0.77958497],
[0.66749069, 0.66749069],
[0.77958497, 0.77958497],
[0.77958497, 0.77958497],
[0.77958497, 0.77958497],
[0.66749069, 0.66749069],
[0.77958497, 0.77958497],
[0.77958497, 0.77958497],
[0.77958497, 0.77958497],
[0.66749069, 0.66749069],
[0.77958497, 0.77958497],
[0.77958497, 0.77958497],
[0.77958497, 0.77958497],
[0.66749069, 0.66749069]
]
)
np.testing.assert_allclose(thrusts, thrusts_truth)

def test_power():
Expand Down Expand Up @@ -222,7 +221,7 @@ def test_axial_induction():
turbine_type_map = turbine_type_map[None, :]
condition = (2, 1)

baseline_ai = np.array([[0.26447651]])
baseline_ai = np.array([[0.26551081]])

# Single turbine
wind_speed = 10.0
Expand Down
19 changes: 14 additions & 5 deletions tests/turbine_operation_models_unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,11 @@ def test_CosineLossTurbine():
tilt_angles=tilt_angles_test,
tilt_interp=None
)
absolute_tilt = tilt_angles_test - turbine_data["power_thrust_table"]["ref_tilt"]
assert test_Ct == baseline_Ct * cosd(yaw_angles_test) * cosd(absolute_tilt)
assert test_Ct == (
baseline_Ct * cosd(yaw_angles_test)
* cosd(tilt_angles_test)
/ cosd(turbine_data["power_thrust_table"]["ref_tilt"])
)


# Check that thrust coefficient works as expected
Expand All @@ -200,7 +203,8 @@ def test_CosineLossTurbine():
)
baseline_misalignment_loss = (
cosd(yaw_angles_nom)
* cosd(tilt_angles_nom - turbine_data["power_thrust_table"]["ref_tilt"])
* cosd(tilt_angles_nom)
/ cosd(turbine_data["power_thrust_table"]["ref_tilt"])
)
baseline_ai = (
1 - np.sqrt(1 - turbine_data["power_thrust_table"]["thrust_coefficient"][truth_index])
Expand All @@ -216,8 +220,13 @@ def test_CosineLossTurbine():
tilt_angles=tilt_angles_test,
tilt_interp=None
)
absolute_tilt = tilt_angles_test - turbine_data["power_thrust_table"]["ref_tilt"]
assert test_Ct == baseline_Ct * cosd(yaw_angles_test) * cosd(absolute_tilt)

assert test_Ct == (
baseline_Ct
* cosd(yaw_angles_test)
* cosd(tilt_angles_test)
/ cosd(turbine_data["power_thrust_table"]["ref_tilt"])
)


def test_SimpleDeratingTurbine():
Expand Down