diff --git a/floris/core/rotor_velocity.py b/floris/core/rotor_velocity.py index 1dbbeb1ed..43d4e3077 100644 --- a/floris/core/rotor_velocity.py +++ b/floris/core/rotor_velocity.py @@ -17,19 +17,18 @@ from floris.utilities import cosd -def rotor_velocity_yaw_correction( +def rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw: float, yaw_angles: NDArrayFloat, rotor_effective_velocities: NDArrayFloat, ) -> NDArrayFloat: # Compute the rotor effective velocity adjusting for yaw settings pW = cosine_loss_exponent_yaw / 3.0 # Convert from cosine_loss_exponent_yaw to w - # TODO: cosine loss hard coded rotor_effective_velocities = rotor_effective_velocities * cosd(yaw_angles) ** pW return rotor_effective_velocities -def rotor_velocity_tilt_correction( +def rotor_velocity_tilt_cosine_correction( tilt_angles: NDArrayFloat, ref_tilt: NDArrayFloat, cosine_loss_exponent_tilt: float, @@ -48,7 +47,6 @@ def rotor_velocity_tilt_correction( tilt_angles = np.where(correct_cp_ct_for_tilt, tilt_angles, old_tilt_angle) # Compute the rotor effective velocity adjusting for tilt - # TODO: cosine loss hard coded relative_tilt = tilt_angles - ref_tilt rotor_effective_velocities = ( rotor_effective_velocities @@ -214,14 +212,14 @@ def rotor_effective_velocity( rotor_effective_velocities = (air_density/ref_air_density)**(1/3) * average_velocities # Compute the rotor effective velocity adjusting for yaw settings - rotor_effective_velocities = rotor_velocity_yaw_correction( + rotor_effective_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw, yaw_angle, rotor_effective_velocities ) # Compute the tilt, if using floating turbines - rotor_effective_velocities = rotor_velocity_tilt_correction( + rotor_effective_velocities = rotor_velocity_tilt_cosine_correction( turbine_type_map, tilt_angle, ref_tilt, @@ -232,3 +230,12 @@ def rotor_effective_velocity( ) return rotor_effective_velocities + +def rotor_velocity_air_density_correction( + velocities: NDArrayFloat, + air_density: float, + ref_air_density: float, +) -> NDArrayFloat: + # Produce equivalent velocities at the reference air density + + return (air_density/ref_air_density)**(1/3) * velocities diff --git a/floris/core/turbine/operation_models.py b/floris/core/turbine/operation_models.py index 8fcbdb540..bd592343c 100644 --- a/floris/core/turbine/operation_models.py +++ b/floris/core/turbine/operation_models.py @@ -17,8 +17,9 @@ from floris.core.rotor_velocity import ( average_velocity, compute_tilt_angles_for_floating_turbines, - rotor_velocity_tilt_correction, - rotor_velocity_yaw_correction, + rotor_velocity_air_density_correction, + rotor_velocity_tilt_cosine_correction, + rotor_velocity_yaw_cosine_correction, ) from floris.type_dec import ( NDArrayFloat, @@ -30,15 +31,6 @@ POWER_SETPOINT_DEFAULT = 1e12 POWER_SETPOINT_DISABLED = 0.001 -def rotor_velocity_air_density_correction( - velocities: NDArrayFloat, - air_density: float, - ref_air_density: float, -) -> NDArrayFloat: - # Produce equivalent velocities at the reference air density - # TODO: This could go on BaseTurbineModel - return (air_density/ref_air_density)**(1/3) * velocities - @define class BaseOperationModel(BaseClass): @@ -67,6 +59,9 @@ def thrust_coefficient() -> None: @staticmethod @abstractmethod def axial_induction() -> None: + # TODO: Consider whether we can make a generic axial_induction method + # based purely on thrust_coefficient so that we don't need to implement + # axial_induciton() in individual operation models. raise NotImplementedError("BaseOperationModel.axial_induction") @define @@ -78,8 +73,6 @@ class SimpleTurbine(BaseOperationModel): As with all turbine submodules, implements only static power() and thrust_coefficient() methods, which are called by power() and thrust_coefficient() on turbine.py, respectively. This class is not intended to be instantiated; it simply defines a library of static methods. - - TODO: Should the turbine submodels each implement axial_induction()? """ def power( @@ -174,8 +167,6 @@ class CosineLossTurbine(BaseOperationModel): As with all turbine submodules, implements only static power() and thrust_coefficient() methods, which are called by power() and thrust_coefficient() on turbine.py, respectively. This class is not intended to be instantiated; it simply defines a library of static methods. - - TODO: Should the turbine submodels each implement axial_induction()? """ def power( @@ -211,13 +202,13 @@ def power( ref_air_density=power_thrust_table["ref_air_density"] ) - rotor_effective_velocities = rotor_velocity_yaw_correction( + rotor_effective_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=power_thrust_table["cosine_loss_exponent_yaw"], yaw_angles=yaw_angles, rotor_effective_velocities=rotor_effective_velocities, ) - rotor_effective_velocities = rotor_velocity_tilt_correction( + rotor_effective_velocities = rotor_velocity_tilt_cosine_correction( tilt_angles=tilt_angles, ref_tilt=power_thrust_table["ref_tilt"], cosine_loss_exponent_tilt=power_thrust_table["cosine_loss_exponent_tilt"], @@ -531,7 +522,7 @@ def power( + power_thrust_table['helix_power_c']*base_powers ) *awc_amplitudes**power_thrust_table['helix_a'] - ) ## TODO: Should probably add max function here + ) # TODO: Should probably add max function here if (awc_modes == 'baseline').any(): return base_powers else: diff --git a/floris/core/turbine/turbine.py b/floris/core/turbine/turbine.py index e176e6a05..17fd956e3 100644 --- a/floris/core/turbine/turbine.py +++ b/floris/core/turbine/turbine.py @@ -126,15 +126,6 @@ def power( Returns: NDArrayFloat: The power, in Watts, for each turbine after adjusting for yaw and tilt. """ - # TODO: Change the order of input arguments to be consistent with the other - # utility functions - velocities first... - # Update to power calculation which replaces the fixed cosine_loss_exponent_yaw exponent - # (which applies to the cosine of the yaw misalignment) with an exponent pW, that changes the - # effective wind speed input to the power calculation, rather than scaling the power. This - # better handles power loss to yaw in above rated conditions - # - # Based on the paper "Optimising yaw control at wind farm level" by - # Ervin Bossanyi # Down-select inputs if ix_filter is given if ix_filter is not None: diff --git a/floris/core/wake_deflection/gauss.py b/floris/core/wake_deflection/gauss.py index e19fd147b..8e1f7378f 100644 --- a/floris/core/wake_deflection/gauss.py +++ b/floris/core/wake_deflection/gauss.py @@ -119,7 +119,12 @@ def function( for details on the methods used. Args: - # TODO + x_i (np.array): x-coordinates of turbine i. + y_i (np.array): y-coordinates of turbine i. + yaw_i (np.array): Yaw angle of turbine i. + turbulence_intensity_i (np.array): Turbulence intensity at turbine i. + ct_i (np.array): Thrust coefficient of turbine i. + rotor_diameter_i (float): Rotor diameter of turbine i. Returns: np.array: Deflection field for the wake. diff --git a/floris/core/wake_velocity/empirical_gauss.py b/floris/core/wake_velocity/empirical_gauss.py index 4d8005056..2e22db525 100644 --- a/floris/core/wake_velocity/empirical_gauss.py +++ b/floris/core/wake_velocity/empirical_gauss.py @@ -257,7 +257,7 @@ def rCalt(wind_veer, sigma_y, sigma_z, y, y_i, delta_y, delta_z, z, HH, Ct, def sigmoid_integral(x, center=0, width=1): y = np.zeros_like(x) - #TODO: Can this be made faster? + # TODO: Can this be made faster? above_smoothing_zone = (x-center) > width/2 y[above_smoothing_zone] = (x-center)[above_smoothing_zone] in_smoothing_zone = ((x-center) >= -width/2) & ((x-center) <= width/2) @@ -293,7 +293,7 @@ def awc_added_wake_mixing( awc_wake_denominator ): - ## TODO: Add TI in the mix, finetune amplitude/freq effect + # TODO: Add TI in the mix, finetune amplitude/freq effect if (awc_mode_i == "helix").any(): return awc_amplitude_i[:,:,0,0]**awc_wake_exp/awc_wake_denominator elif (awc_mode_i == "baseline").any(): diff --git a/floris/core/wake_velocity/gauss.py b/floris/core/wake_velocity/gauss.py index 5c73786ae..bac3cf415 100644 --- a/floris/core/wake_velocity/gauss.py +++ b/floris/core/wake_velocity/gauss.py @@ -120,7 +120,7 @@ def function( # Another linear ramp, but positive upstream of the far wake and negative in the # far wake; 0 at the start of the far wake near_wake_ramp_down = (x0 - x) / (x0 - xR) - # near_wake_ramp_down = -1 * (near_wake_ramp_up - 1) # TODO: this is equivalent, right? + # near_wake_ramp_down = -1 * (near_wake_ramp_up - 1) # : this is equivalent, right? sigma_y = near_wake_ramp_down * 0.501 * rotor_diameter_i * np.sqrt(ct_i / 2.0) sigma_y += near_wake_ramp_up * sigma_y0 diff --git a/floris/floris_model.py b/floris/floris_model.py index 709c06884..65d1e1d4b 100644 --- a/floris/floris_model.py +++ b/floris/floris_model.py @@ -576,12 +576,6 @@ def _get_farm_power( Returns: float: Sum of wind turbine powers in W. """ - # TODO: Turbulence correction used in the power calculation, but may not be in - # the model yet - # TODO: Turbines need a switch for using turbulence correction - # TODO: Uncomment out the following two lines once the above are resolved - # for turbine in self.core.farm.turbines: - # turbine.use_turbulence_correction = use_turbulence_correction if use_turbulence_correction: raise NotImplementedError( "Turbulence correction is not yet implemented in the power calculation." diff --git a/floris/optimization/layout_optimization/layout_optimization_base.py b/floris/optimization/layout_optimization/layout_optimization_base.py index 99016d902..dd9afaae3 100644 --- a/floris/optimization/layout_optimization/layout_optimization_base.py +++ b/floris/optimization/layout_optimization/layout_optimization_base.py @@ -81,7 +81,6 @@ def __init__( minimum_yaw_angle=-30.0, maximum_yaw_angle=30.0, ) - # TODO: is this being used? fmodel.run() if self.use_value: diff --git a/floris/parallel_floris_model.py b/floris/parallel_floris_model.py index 4de5015df..ea235aaae 100644 --- a/floris/parallel_floris_model.py +++ b/floris/parallel_floris_model.py @@ -263,7 +263,7 @@ def _postprocessing(self, output): return turbine_powers - def run(self): # TODO: Remove or update this function? + def run(self): raise UserWarning( "'run' not supported on ParallelFlorisModel. Please use " "'get_turbine_powers' or 'get_farm_power' directly." diff --git a/floris/uncertain_floris_model.py b/floris/uncertain_floris_model.py index 217dab2e5..be37d902c 100644 --- a/floris/uncertain_floris_model.py +++ b/floris/uncertain_floris_model.py @@ -325,12 +325,6 @@ def _get_farm_power( Returns: float: Sum of wind turbine powers in W. """ - # TODO: Turbulence correction used in the power calculation, but may not be in - # the model yet - # TODO: Turbines need a switch for using turbulence correction - # TODO: Uncomment out the following two lines once the above are resolved - # for turbine in self.core.farm.turbines: - # turbine.use_turbulence_correction = use_turbulence_correction if use_turbulence_correction: raise NotImplementedError( "Turbulence correction is not yet implemented in the power calculation." diff --git a/tests/rotor_velocity_unit_test.py b/tests/rotor_velocity_unit_test.py index 468b7a887..a83ed219e 100644 --- a/tests/rotor_velocity_unit_test.py +++ b/tests/rotor_velocity_unit_test.py @@ -6,21 +6,41 @@ compute_tilt_angles_for_floating_turbines, compute_tilt_angles_for_floating_turbines_map, cubic_cubature, - rotor_velocity_tilt_correction, - rotor_velocity_yaw_correction, + rotor_velocity_air_density_correction, + rotor_velocity_tilt_cosine_correction, + rotor_velocity_yaw_cosine_correction, simple_cubature, ) from tests.conftest import SampleInputs, WIND_SPEEDS -def test_rotor_velocity_yaw_correction(): +def test_rotor_velocity_air_density_correction(): + + wind_speed = 10. + ref_air_density = 1.225 + test_density = 1.2 + + test_speed = rotor_velocity_air_density_correction(wind_speed, ref_air_density, ref_air_density) + assert test_speed == wind_speed + + test_speed = rotor_velocity_air_density_correction(wind_speed, test_density, test_density) + assert test_speed == wind_speed + + test_speed = rotor_velocity_air_density_correction(0., test_density, ref_air_density) + assert test_speed == 0. + + test_speed = rotor_velocity_air_density_correction(wind_speed, test_density, ref_air_density) + assert np.allclose((test_speed/wind_speed)**3, test_density/ref_air_density) + + +def test_rotor_velocity_yaw_cosine_correction(): N_TURBINES = 4 wind_speed = average_velocity(10.0 * np.ones((1, 1, 3, 3))) wind_speed_N_TURBINES = average_velocity(10.0 * np.ones((1, N_TURBINES, 3, 3))) # Test a single turbine for zero yaw - yaw_corrected_velocities = rotor_velocity_yaw_correction( + yaw_corrected_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=3.0, yaw_angles=0.0, rotor_effective_velocities=wind_speed, @@ -28,7 +48,7 @@ def test_rotor_velocity_yaw_correction(): np.testing.assert_allclose(yaw_corrected_velocities, wind_speed) # Test a single turbine for non-zero yaw - yaw_corrected_velocities = rotor_velocity_yaw_correction( + yaw_corrected_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=3.0, yaw_angles=60.0, rotor_effective_velocities=wind_speed, @@ -36,7 +56,7 @@ def test_rotor_velocity_yaw_correction(): np.testing.assert_allclose(yaw_corrected_velocities, 0.5 * wind_speed) # Test multiple turbines for zero yaw - yaw_corrected_velocities = rotor_velocity_yaw_correction( + yaw_corrected_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=3.0, yaw_angles=np.zeros((1, N_TURBINES)), rotor_effective_velocities=wind_speed_N_TURBINES, @@ -44,7 +64,7 @@ def test_rotor_velocity_yaw_correction(): np.testing.assert_allclose(yaw_corrected_velocities, wind_speed_N_TURBINES) # Test multiple turbines for non-zero yaw - yaw_corrected_velocities = rotor_velocity_yaw_correction( + yaw_corrected_velocities = rotor_velocity_yaw_cosine_correction( cosine_loss_exponent_yaw=3.0, yaw_angles=np.ones((1, N_TURBINES)) * 60.0, rotor_effective_velocities=wind_speed_N_TURBINES, @@ -52,7 +72,7 @@ def test_rotor_velocity_yaw_correction(): np.testing.assert_allclose(yaw_corrected_velocities, 0.5 * wind_speed_N_TURBINES) -def test_rotor_velocity_tilt_correction(): +def test_rotor_velocity_tilt_cosine_correction(): N_TURBINES = 4 wind_speed = average_velocity(10.0 * np.ones((1, 1, 3, 3))) @@ -66,7 +86,7 @@ def test_rotor_velocity_tilt_correction(): turbine_type_map = turbine_type_map[None, :] # Test single non-floating turbine - tilt_corrected_velocities = rotor_velocity_tilt_correction( + tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( #turbine_type_map=np.array([turbine_type_map[:, 0]]), tilt_angles=5.0*np.ones((1, 1)), ref_tilt=np.array([turbine.power_thrust_table["ref_tilt"]]), @@ -81,7 +101,7 @@ def test_rotor_velocity_tilt_correction(): np.testing.assert_allclose(tilt_corrected_velocities, wind_speed) # Test multiple non-floating turbines - tilt_corrected_velocities = rotor_velocity_tilt_correction( + tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( #turbine_type_map=turbine_type_map, tilt_angles=5.0*np.ones((1, N_TURBINES)), ref_tilt=np.array([turbine.power_thrust_table["ref_tilt"]] * N_TURBINES), @@ -96,7 +116,7 @@ def test_rotor_velocity_tilt_correction(): np.testing.assert_allclose(tilt_corrected_velocities, wind_speed_N_TURBINES) # Test single floating turbine - tilt_corrected_velocities = rotor_velocity_tilt_correction( + tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( #turbine_type_map=np.array([turbine_type_map[:, 0]]), tilt_angles=5.0*np.ones((1, 1)), ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]]), @@ -111,7 +131,7 @@ def test_rotor_velocity_tilt_correction(): np.testing.assert_allclose(tilt_corrected_velocities, wind_speed) # Test multiple floating turbines - tilt_corrected_velocities = rotor_velocity_tilt_correction( + tilt_corrected_velocities = rotor_velocity_tilt_cosine_correction( #turbine_type_map, tilt_angles=5.0*np.ones((1, N_TURBINES)), ref_tilt=np.array([turbine_floating.power_thrust_table["ref_tilt"]] * N_TURBINES), diff --git a/tests/turbine_operation_models_integration_test.py b/tests/turbine_operation_models_integration_test.py index bd9dc3930..db4f0cc41 100644 --- a/tests/turbine_operation_models_integration_test.py +++ b/tests/turbine_operation_models_integration_test.py @@ -6,7 +6,6 @@ CosineLossTurbine, MixedOperationTurbine, POWER_SETPOINT_DEFAULT, - rotor_velocity_air_density_correction, SimpleDeratingTurbine, SimpleTurbine, ) @@ -14,24 +13,6 @@ from tests.conftest import SampleInputs, WIND_SPEEDS -def test_rotor_velocity_air_density_correction(): - - wind_speed = 10. - ref_air_density = 1.225 - test_density = 1.2 - - test_speed = rotor_velocity_air_density_correction(wind_speed, ref_air_density, ref_air_density) - assert test_speed == wind_speed - - test_speed = rotor_velocity_air_density_correction(wind_speed, test_density, test_density) - assert test_speed == wind_speed - - test_speed = rotor_velocity_air_density_correction(0., test_density, ref_air_density) - assert test_speed == 0. - - test_speed = rotor_velocity_air_density_correction(wind_speed, test_density, ref_air_density) - assert np.allclose((test_speed/wind_speed)**3, test_density/ref_air_density) - def test_submodel_attributes(): assert hasattr(SimpleTurbine, "power") diff --git a/tests/turbine_unit_test.py b/tests/turbine_unit_test.py index 2161a7309..ca5e73777 100644 --- a/tests/turbine_unit_test.py +++ b/tests/turbine_unit_test.py @@ -60,12 +60,6 @@ def test_turbine_init(): assert turbine.rotor_radius == turbine.rotor_diameter / 2.0 assert turbine.rotor_area == np.pi * turbine.rotor_radius ** 2.0 - # TODO: test these explicitly. - # Test create a simpler interpolator and test that you get the values you expect - # fCt_interp: interp1d = field(init=False) - # power_function: interp1d = field(init=False) - # tilt_interp: interp1d = field(init=False, default=None) - assert callable(turbine.thrust_coefficient_function) assert callable(turbine.power_function)