Skip to content

Commit

Permalink
Revert "Allow yaw optimization with disabled turbines (#1027)"
Browse files Browse the repository at this point in the history
This reverts commit 64f9dfa.
  • Loading branch information
misi9170 authored Nov 18, 2024
1 parent 64f9dfa commit 077fdbb
Show file tree
Hide file tree
Showing 10 changed files with 43 additions and 376 deletions.

This file was deleted.

92 changes: 31 additions & 61 deletions floris/core/turbine/operation_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -382,22 +382,22 @@ def axial_induction(
@define
class MixedOperationTurbine(BaseOperationModel):

@staticmethod
def power(
yaw_angles: NDArrayFloat,
power_setpoints: NDArrayFloat,
**kwargs
):
(
yaw_angles,
power_setpoints,
yaw_angles_mask,
power_setpoints_mask,
neither_mask
) = MixedOperationTurbine._handle_mixed_operation_setpoints(
yaw_angles=yaw_angles,
power_setpoints=power_setpoints
)
# Yaw angles mask all yaw_angles not equal to zero
yaw_angles_mask = yaw_angles != 0.0
power_setpoints_mask = power_setpoints < POWER_SETPOINT_DEFAULT
neither_mask = np.logical_not(yaw_angles_mask) & np.logical_not(power_setpoints_mask)

if (power_setpoints_mask & yaw_angles_mask).any():
raise ValueError((
"Power setpoints and yaw angles are incompatible."
"If yaw_angles entry is nonzero, power_setpoints must be greater than"
" or equal to {0}.".format(POWER_SETPOINT_DEFAULT)
))

powers = np.zeros_like(power_setpoints)
powers[yaw_angles_mask] += CosineLossTurbine.power(
Expand All @@ -414,22 +414,21 @@ def power(

return powers

@staticmethod
def thrust_coefficient(
yaw_angles: NDArrayFloat,
power_setpoints: NDArrayFloat,
**kwargs
):
(
yaw_angles,
power_setpoints,
yaw_angles_mask,
power_setpoints_mask,
neither_mask
) = MixedOperationTurbine._handle_mixed_operation_setpoints(
yaw_angles=yaw_angles,
power_setpoints=power_setpoints
)
yaw_angles_mask = yaw_angles != 0.0
power_setpoints_mask = power_setpoints < POWER_SETPOINT_DEFAULT
neither_mask = np.logical_not(yaw_angles_mask) & np.logical_not(power_setpoints_mask)

if (power_setpoints_mask & yaw_angles_mask).any():
raise ValueError((
"Power setpoints and yaw angles are incompatible."
"If yaw_angles entry is nonzero, power_setpoints must be greater than"
" or equal to {0}.".format(POWER_SETPOINT_DEFAULT)
))

thrust_coefficients = np.zeros_like(power_setpoints)
thrust_coefficients[yaw_angles_mask] += CosineLossTurbine.thrust_coefficient(
Expand All @@ -446,22 +445,21 @@ def thrust_coefficient(

return thrust_coefficients

@staticmethod
def axial_induction(
yaw_angles: NDArrayFloat,
power_setpoints: NDArrayFloat,
**kwargs
):
(
yaw_angles,
power_setpoints,
yaw_angles_mask,
power_setpoints_mask,
neither_mask
) = MixedOperationTurbine._handle_mixed_operation_setpoints(
yaw_angles=yaw_angles,
power_setpoints=power_setpoints
)
yaw_angles_mask = yaw_angles != 0.0
power_setpoints_mask = power_setpoints < POWER_SETPOINT_DEFAULT
neither_mask = np.logical_not(yaw_angles_mask) & np.logical_not(power_setpoints_mask)

if (power_setpoints_mask & yaw_angles_mask).any():
raise ValueError((
"Power setpoints and yaw angles are incompatible."
"If yaw_angles entry is nonzero, power_setpoints must be greater than"
" or equal to {0}.".format(POWER_SETPOINT_DEFAULT)
))

axial_inductions = np.zeros_like(power_setpoints)
axial_inductions[yaw_angles_mask] += CosineLossTurbine.axial_induction(
Expand All @@ -478,34 +476,6 @@ def axial_induction(

return axial_inductions

@staticmethod
def _handle_mixed_operation_setpoints(
yaw_angles: NDArrayFloat,
power_setpoints: NDArrayFloat,
):
"""
Check for incompatible yaw angles and power setpoints and raise an error if found.
Return masks and updated setpoints.
"""
# If any turbines are disabled, set their yaw angles to zero
yaw_angles[power_setpoints <= POWER_SETPOINT_DISABLED] = 0.0

# Create masks for whether yaw angles and power setpoints are set
yaw_angles_mask = yaw_angles != 0.0
power_setpoints_mask = power_setpoints < POWER_SETPOINT_DEFAULT
neither_mask = np.logical_not(yaw_angles_mask) & np.logical_not(power_setpoints_mask)

# Check for incompatibility and raise error if found.
if (power_setpoints_mask & yaw_angles_mask).any():
raise ValueError((
"Power setpoints and yaw angles are incompatible."
"If yaw_angles entry is nonzero, power_setpoints must be greater than"
" or equal to {0}.".format(POWER_SETPOINT_DEFAULT)
))

# Return updated setpoints as well as masks
return yaw_angles, power_setpoints, yaw_angles_mask, power_setpoints_mask, neither_mask

@define
class AWCTurbine(BaseOperationModel):
"""
Expand Down
5 changes: 4 additions & 1 deletion floris/floris_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,10 @@ def set(
# previous setting
if not (_yaw_angles == 0).all():
self.core.farm.set_yaw_angles(_yaw_angles)
if not (_power_setpoints == POWER_SETPOINT_DEFAULT).all():
if not (
(_power_setpoints == POWER_SETPOINT_DEFAULT)
| (_power_setpoints == POWER_SETPOINT_DISABLED)
).all():
self.core.farm.set_power_setpoints(_power_setpoints)
if _awc_modes is not None:
self.core.farm.set_awc_modes(_awc_modes)
Expand Down
12 changes: 2 additions & 10 deletions floris/optimization/yaw_optimization/yaw_optimization_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import numpy as np
import pandas as pd

from floris.core.turbine.operation_models import POWER_SETPOINT_DISABLED
from floris.logging_manager import LoggingManager

from .yaw_optimization_tools import derive_downstream_turbines
Expand Down Expand Up @@ -100,7 +99,7 @@ def __init__(
"""

# Save turbine object to self
self.fmodel = copy.deepcopy(fmodel)
self.fmodel = fmodel.copy()
self.nturbs = len(self.fmodel.layout_x)

# # Check floris options
Expand Down Expand Up @@ -132,11 +131,6 @@ def __init__(
self.minimum_yaw_angle = self._unpack_variable(minimum_yaw_angle)
self.maximum_yaw_angle = self._unpack_variable(maximum_yaw_angle)

# Limit yaw angles to zero for disabled turbines
active_turbines = fmodel.core.farm.power_setpoints > POWER_SETPOINT_DISABLED
self.minimum_yaw_angle[~active_turbines] = 0.0
self.maximum_yaw_angle[~active_turbines] = 0.0

# Set initial condition for optimization
if x0 is not None:
self.x0 = self._unpack_variable(x0)
Expand Down Expand Up @@ -230,7 +224,7 @@ def _reduce_control_problem(self):
self.turbs_to_opt = (self.maximum_yaw_angle - self.minimum_yaw_angle >= 0.001)

# Initialize subset variables as full set
self.fmodel_subset = copy.deepcopy(self.fmodel)
self.fmodel_subset = self.fmodel.copy()
n_findex_subset = copy.deepcopy(self.fmodel.core.flow_field.n_findex)
minimum_yaw_angle_subset = copy.deepcopy(self.minimum_yaw_angle)
maximum_yaw_angle_subset = copy.deepcopy(self.maximum_yaw_angle)
Expand Down Expand Up @@ -307,7 +301,6 @@ def _calculate_farm_power(
ti_array=None,
turbine_weights=None,
heterogeneous_speed_multipliers=None,
power_setpoints=None,
):
"""
Calculate the wind farm power production assuming the predefined
Expand Down Expand Up @@ -360,7 +353,6 @@ def _calculate_farm_power(
wind_speeds=ws_array,
turbulence_intensities=ti_array,
yaw_angles=yaw_angles,
power_setpoints=power_setpoints,
)
fmodel_subset.run()
turbine_power = fmodel_subset.get_turbine_powers()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

import numpy as np

from floris.core.turbine.operation_models import POWER_SETPOINT_DISABLED
from floris.utilities import rotate_coordinates_rel_west

from .yaw_optimization_base import YawOptimization
Expand Down Expand Up @@ -47,11 +46,10 @@ def optimize(self):
# Loop through every WD individually. WS ignored!
wd_array = self.fmodel_subset.core.flow_field.wind_directions

active_turbines = self.fmodel_subset.core.farm.power_setpoints > POWER_SETPOINT_DISABLED
for nwdi, wd in enumerate(wd_array):
self._yaw_angles_opt_subset[nwdi, active_turbines[nwdi]] = geometric_yaw(
self.fmodel_subset.layout_x[active_turbines[nwdi]],
self.fmodel_subset.layout_y[active_turbines[nwdi]],
self._yaw_angles_opt_subset[nwdi, :] = geometric_yaw(
self.fmodel_subset.layout_x,
self.fmodel_subset.layout_y,
wd,
self.fmodel.core.farm.turbine_definitions[0]["rotor_diameter"],
top_left_yaw_upper=self.maximum_yaw_angle[0, 0],
Expand Down
6 changes: 0 additions & 6 deletions floris/optimization/yaw_optimization/yaw_optimizer_scipy.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,6 @@ def __init__(
Instantiate YawOptimizationScipy object with a FlorisModel object
and assign parameter values.
"""
valid_op_models = ["cosine-loss"]
if fmodel.get_operation_model() not in valid_op_models:
raise ValueError(
"YawOptimizationScipy is currently limited to the following operation models: "
+ ", ".join(valid_op_models)
)
if opt_options is None:
# Default SciPy parameters
opt_options = {
Expand Down
5 changes: 1 addition & 4 deletions floris/optimization/yaw_optimization/yaw_optimizer_sr.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ def _calc_powers_with_memory(self, yaw_angles_subset, use_memory=True):
wd_array_subset = self.fmodel_subset.core.flow_field.wind_directions
ws_array_subset = self.fmodel_subset.core.flow_field.wind_speeds
ti_array_subset = self.fmodel_subset.core.flow_field.turbulence_intensities
power_setpoints_subset = self.fmodel_subset.core.farm.power_setpoints
turbine_weights_subset = self._turbine_weights_subset

# Reformat yaw_angles_subset, if necessary
Expand All @@ -109,7 +108,6 @@ def _calc_powers_with_memory(self, yaw_angles_subset, use_memory=True):
wd_array_subset = np.tile(wd_array_subset, Ny)
ws_array_subset = np.tile(ws_array_subset, Ny)
ti_array_subset = np.tile(ti_array_subset, Ny)
power_setpoints_subset = np.tile(power_setpoints_subset, (Ny, 1))
turbine_weights_subset = np.tile(turbine_weights_subset, (Ny, 1))

# Initialize empty matrix for floris farm power outputs
Expand Down Expand Up @@ -145,8 +143,7 @@ def _calc_powers_with_memory(self, yaw_angles_subset, use_memory=True):
ti_array=ti_array_subset[~idx],
turbine_weights=turbine_weights_subset[~idx, :],
yaw_angles=yaw_angles_subset[~idx, :],
heterogeneous_speed_multipliers=het_sm,
power_setpoints=power_setpoints_subset[~idx, :],
heterogeneous_speed_multipliers=het_sm
)
self.time_spent_in_floris += (timerpc() - start_time)

Expand Down
21 changes: 2 additions & 19 deletions tests/floris_model_integration_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
TimeSeries,
WindRose,
)
from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT, POWER_SETPOINT_DISABLED
from floris.core.turbine.operation_models import POWER_SETPOINT_DEFAULT


TEST_DATA = Path(__file__).resolve().parent / "data"
Expand Down Expand Up @@ -44,24 +44,7 @@ def test_assign_setpoints():

# power_setpoints and disable_turbines (disable_turbines overrides power_setpoints)
fmodel.set(power_setpoints=[[1e6, 2e6]], disable_turbines=[[True, False]])
assert np.allclose(fmodel.core.farm.power_setpoints, np.array([[POWER_SETPOINT_DISABLED, 2e6]]))

# Setting sequentially is equivalent to setting together
fmodel.reset_operation()
fmodel.set(disable_turbines=[[True, False]])
fmodel.set(yaw_angles=[[0, 30]])
assert np.allclose(
fmodel.core.farm.power_setpoints,
np.array([[POWER_SETPOINT_DISABLED, POWER_SETPOINT_DEFAULT]])
)
assert np.allclose(fmodel.core.farm.yaw_angles, np.array([[0, 30]]))

fmodel.set(disable_turbines=[[True, False]], yaw_angles=[[0, 30]])
assert np.allclose(
fmodel.core.farm.power_setpoints,
np.array([[POWER_SETPOINT_DISABLED, POWER_SETPOINT_DEFAULT]])
)
assert np.allclose(fmodel.core.farm.yaw_angles, np.array([[0, 30]]))
assert np.allclose(fmodel.core.farm.power_setpoints, np.array([[0.001, 2e6]]))

def test_set_run():
"""
Expand Down
Loading

0 comments on commit 077fdbb

Please sign in to comment.