diff --git a/examples/40_test_derating.py b/examples/40_test_derating.py new file mode 100644 index 000000000..59a587259 --- /dev/null +++ b/examples/40_test_derating.py @@ -0,0 +1,116 @@ +# Copyright 2024 NREL + +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# See https://floris.readthedocs.io for documentation + +import matplotlib.pyplot as plt +import numpy as np +import yaml + +from floris.tools import FlorisInterface + + +""" +Example to test out derating of turbines and mixed derating and yawing. Will be refined before +release. TODO: Demonstrate shutting off turbines also, once developed. +""" + +# Grab model of FLORIS and update to deratable turbines +fi = FlorisInterface("inputs/gch.yaml") + +with open(str( + fi.floris.as_dict()["farm"]["turbine_library_path"] / + (fi.floris.as_dict()["farm"]["turbine_type"][0] + ".yaml") +)) as t: + turbine_type = yaml.safe_load(t) +turbine_type["power_thrust_model"] = "simple-derating" + +# Convert to a simple two turbine layout with derating turbines +fi.reinitialize(layout_x=[0, 1000.0], layout_y=[0.0, 0.0], turbine_type=[turbine_type]) + +# Set the wind directions and speeds to be constant over n_findex = N time steps +N = 50 +fi.reinitialize(wind_directions=270 * np.ones(N), wind_speeds=10.0 * np.ones(N)) +fi.calculate_wake() +turbine_powers_orig = fi.get_turbine_powers() + +# Add derating +power_setpoints = np.tile(np.linspace(1, 6e6, N), 2).reshape(2, N).T +fi.calculate_wake(power_setpoints=power_setpoints) +turbine_powers_derated = fi.get_turbine_powers() + +# Compute available power at downstream turbine +power_setpoints_2 = np.array([np.linspace(1, 6e6, N), np.full(N, None)]).T +fi.calculate_wake(power_setpoints=power_setpoints_2) +turbine_powers_avail_ds = fi.get_turbine_powers()[:,1] + +# Plot the results +fig, ax = plt.subplots(1, 1) +ax.plot(power_setpoints[:, 0]/1000, turbine_powers_derated[:, 0]/1000, color="C0", label="Upstream") +ax.plot( + power_setpoints[:, 1]/1000, + turbine_powers_derated[:, 1]/1000, + color="C1", + label="Downstream" +) +ax.plot( + power_setpoints[:, 0]/1000, + turbine_powers_orig[:, 0]/1000, + color="C0", + linestyle="dotted", + label="Upstream available" +) +ax.plot( + power_setpoints[:, 1]/1000, + turbine_powers_avail_ds/1000, + color="C1", + linestyle="dotted", label="Downstream available" +) +ax.plot( + power_setpoints[:, 1]/1000, + np.ones(N)*np.max(turbine_type["power_thrust_table"]["power"]), + color="k", + linestyle="dashed", + label="Rated power" +) +ax.grid() +ax.legend() +ax.set_xlim([0, 6e3]) +ax.set_xlabel("Power setpoint (kW)") +ax.set_ylabel("Power produced (kW)") + +# Second example showing mixed model use. +turbine_type["power_thrust_model"] = "mixed" +yaw_angles = np.array([ + [0.0, 0.0], + [0.0, 0.0], + [20.0, 10.0], + [0.0, 10.0], + [20.0, 0.0] +]) +power_setpoints = np.array([ + [None, None], + [2e6, 1e6], + [None, None], + [2e6, None,], + [None, 1e6] +]) +fi.reinitialize( + wind_directions=270 * np.ones(len(yaw_angles)), + wind_speeds=10.0 * np.ones(len(yaw_angles)), + turbine_type=[turbine_type]*2 +) +fi.calculate_wake(yaw_angles=yaw_angles, power_setpoints=power_setpoints) +turbine_powers = fi.get_turbine_powers() +print(turbine_powers) + +plt.show() diff --git a/floris/simulation/farm.py b/floris/simulation/farm.py index 7544231fe..56e20d819 100644 --- a/floris/simulation/farm.py +++ b/floris/simulation/farm.py @@ -32,6 +32,7 @@ Turbine, ) from floris.simulation.rotor_velocity import compute_tilt_angles_for_floating_turbines_map +from floris.simulation.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.type_dec import ( convert_to_path, floris_array_converter, @@ -92,6 +93,9 @@ class Farm(BaseClass): tilt_angles: NDArrayFloat = field(init=False) tilt_angles_sorted: NDArrayFloat = field(init=False) + power_setpoints: NDArrayFloat = field(init=False) + power_setpoints_sorted: NDArrayFloat = field(init=False) + hub_heights: NDArrayFloat = field(init=False) hub_heights_sorted: NDArrayFloat = field(init=False, factory=list) @@ -233,6 +237,11 @@ def initialize(self, sorted_indices): sorted_indices[:, :, 0, 0], axis=1, ) + self.power_setpoints_sorted = np.take_along_axis( + self.power_setpoints, + sorted_indices[:, :, 0, 0], + axis=1, + ) self.state = State.INITIALIZED def construct_hub_heights(self): @@ -341,6 +350,10 @@ def set_tilt_to_ref_tilt(self, n_findex: int): * self.ref_tilts ) + def set_power_setpoints(self, n_findex: int): + self.power_setpoints = POWER_SETPOINT_DEFAULT * np.ones((n_findex, self.n_turbines)) + self.power_setpoints_sorted = POWER_SETPOINT_DEFAULT * np.ones((n_findex, self.n_turbines)) + def calculate_tilt_for_eff_velocities(self, rotor_effective_velocities): tilt_angles = compute_tilt_angles_for_floating_turbines_map( self.turbine_type_map_sorted, diff --git a/floris/simulation/floris.py b/floris/simulation/floris.py index e2e475e0e..f0a492f6a 100644 --- a/floris/simulation/floris.py +++ b/floris/simulation/floris.py @@ -98,6 +98,7 @@ def __attrs_post_init__(self) -> None: self.farm.construct_turbine_correct_cp_ct_for_tilt() self.farm.set_yaw_angles(self.flow_field.n_findex) self.farm.set_tilt_to_ref_tilt(self.flow_field.n_findex) + self.farm.set_power_setpoints(self.flow_field.n_findex) if self.solver["type"] == "turbine_grid": self.grid = TurbineGrid( diff --git a/floris/simulation/solver.py b/floris/simulation/solver.py index c80f355cc..92da51959 100644 --- a/floris/simulation/solver.py +++ b/floris/simulation/solver.py @@ -101,8 +101,10 @@ def sequential_solver( ct_i = thrust_coefficient( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -118,8 +120,10 @@ def sequential_solver( ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, axial_induction_functions=farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -330,8 +334,10 @@ def full_flow_sequential_solver( ct_i = thrust_coefficient( velocities=turbine_grid_flow_field.u_sorted, + air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, + power_setpoints=turbine_grid_farm.power_setpoints_sorted, thrust_coefficient_functions=turbine_grid_farm.turbine_thrust_coefficient_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, @@ -344,8 +350,10 @@ def full_flow_sequential_solver( ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, + air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, + power_setpoints=turbine_grid_farm.power_setpoints_sorted, axial_induction_functions=turbine_grid_farm.turbine_axial_induction_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, @@ -495,8 +503,10 @@ def cc_solver( turb_avg_vels = average_velocity(turb_inflow_field) turb_Cts = thrust_coefficient( turb_avg_vels, + flow_field.air_density, farm.yaw_angles_sorted, farm.tilt_angles_sorted, + farm.power_setpoints_sorted, farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -508,8 +518,10 @@ def cc_solver( turb_Cts = turb_Cts[:, :, None, None] turb_aIs = axial_induction( turb_avg_vels, + flow_field.air_density, farm.yaw_angles_sorted, farm.tilt_angles_sorted, + farm.power_setpoints_sorted, farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -526,8 +538,10 @@ def cc_solver( axial_induction_i = axial_induction( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, axial_induction_functions=farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -737,8 +751,10 @@ def full_flow_cc_solver( turb_avg_vels = average_velocity(turbine_grid_flow_field.u_sorted) turb_Cts = thrust_coefficient( velocities=turb_avg_vels, + air_density=flow_field_grid.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, + power_setpoints=turbine_grid_farm.power_setpoints_sorted, thrust_coefficient_functions=turbine_grid_farm.turbine_thrust_coefficient_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, @@ -751,8 +767,10 @@ def full_flow_cc_solver( axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, + air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, + power_setpoints=turbine_grid_farm.power_setpoints_sorted, axial_induction_functions=turbine_grid_farm.turbine_axial_induction_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, @@ -891,8 +909,10 @@ def turbopark_solver( Cts = thrust_coefficient( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -904,8 +924,10 @@ def turbopark_solver( ct_i = thrust_coefficient( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -920,8 +942,10 @@ def turbopark_solver( ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, axial_induction_functions=farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -978,8 +1002,10 @@ def turbopark_solver( turbulence_intensity_ii = turbine_turbulence_intensity[:, ii:ii+1] ct_ii = thrust_coefficient( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -1174,8 +1200,10 @@ def empirical_gauss_solver( ct_i = thrust_coefficient( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, thrust_coefficient_functions=farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -1190,8 +1218,10 @@ def empirical_gauss_solver( ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, + air_density=flow_field.air_density, yaw_angles=farm.yaw_angles_sorted, tilt_angles=farm.tilt_angles_sorted, + power_setpoints=farm.power_setpoints_sorted, axial_induction_functions=farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, @@ -1375,8 +1405,10 @@ def full_flow_empirical_gauss_solver( ct_i = thrust_coefficient( velocities=turbine_grid_flow_field.u_sorted, + air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, + power_setpoints=turbine_grid_farm.power_setpoints_sorted, thrust_coefficient_functions=turbine_grid_farm.turbine_thrust_coefficient_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, @@ -1389,8 +1421,10 @@ def full_flow_empirical_gauss_solver( ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, + air_density=turbine_grid_flow_field.air_density, yaw_angles=turbine_grid_farm.yaw_angles_sorted, tilt_angles=turbine_grid_farm.tilt_angles_sorted, + power_setpoints=turbine_grid_farm.power_setpoints_sorted, axial_induction_functions=turbine_grid_farm.turbine_axial_induction_functions, tilt_interps=turbine_grid_farm.turbine_tilt_interps, correct_cp_ct_for_tilt=turbine_grid_farm.correct_cp_ct_for_tilt_sorted, diff --git a/floris/simulation/turbine/__init__.py b/floris/simulation/turbine/__init__.py index f1ccca6d0..355f5c2df 100644 --- a/floris/simulation/turbine/__init__.py +++ b/floris/simulation/turbine/__init__.py @@ -14,5 +14,7 @@ from floris.simulation.turbine.operation_models import ( CosineLossTurbine, + MixedOperationTurbine, + SimpleDeratingTurbine, SimpleTurbine, ) diff --git a/floris/simulation/turbine/operation_models.py b/floris/simulation/turbine/operation_models.py index 93173f364..82c11ee70 100644 --- a/floris/simulation/turbine/operation_models.py +++ b/floris/simulation/turbine/operation_models.py @@ -40,6 +40,8 @@ from floris.utilities import cosd +POWER_SETPOINT_DEFAULT = 1e12 + def rotor_velocity_air_density_correction( velocities: NDArrayFloat, air_density: float, @@ -315,3 +317,181 @@ def axial_induction( misalignment_loss = cosd(yaw_angles) * cosd(tilt_angles - power_thrust_table["ref_tilt"]) return 0.5 / misalignment_loss * (1 - np.sqrt(1 - thrust_coefficient * misalignment_loss)) + +@define +class SimpleDeratingTurbine(BaseOperationModel): + """ + power_thrust_table is a dictionary (normally defined on the turbine input yaml) + that contains the parameters necessary to evaluate power(), thrust(), and axial_induction(). + Any specific parameters for derating can be placed here. (they can be added to the turbine + yaml). For this operation model to receive those arguements, they'll need to be + added to the kwargs dictionaries in the respective functions on turbine.py. They won't affect + the other operation models. + """ + def power( + power_thrust_table: dict, + velocities: NDArrayFloat, + air_density: float, + power_setpoints: NDArrayFloat | None, + average_method: str = "cubic-mean", + cubature_weights: NDArrayFloat | None = None, + **_ # <- Allows other models to accept other keyword arguments + ): + base_powers = SimpleTurbine.power( + power_thrust_table=power_thrust_table, + velocities=velocities, + air_density=air_density, + average_method=average_method, + cubature_weights=cubature_weights + ) + if power_setpoints is None: + return base_powers + else: + return np.minimum(base_powers, power_setpoints) + + # TODO: would we like special handling of zero power setpoints + # (mixed with non-zero values) to speed up computation in that case? + + def thrust_coefficient( + power_thrust_table: dict, + velocities: NDArrayFloat, + air_density: float, + power_setpoints: NDArrayFloat, + average_method: str = "cubic-mean", + cubature_weights: NDArrayFloat | None = None, + **_ # <- Allows other models to accept other keyword arguments + ): + base_thrust_coefficients = SimpleTurbine.thrust_coefficient( + power_thrust_table=power_thrust_table, + velocities=velocities, + average_method=average_method, + cubature_weights=cubature_weights + ) + if power_setpoints is None: + return base_thrust_coefficients + else: + # Assume thrust coefficient scales directly with power + base_powers = SimpleTurbine.power( + power_thrust_table=power_thrust_table, + velocities=velocities, + air_density=air_density + ) + power_fractions = power_setpoints / base_powers + thrust_coefficients = power_fractions * base_thrust_coefficients + return np.minimum(base_thrust_coefficients, thrust_coefficients) + + def axial_induction( + power_thrust_table: dict, + velocities: NDArrayFloat, + air_density: float, + power_setpoints: NDArrayFloat, + average_method: str = "cubic-mean", + cubature_weights: NDArrayFloat | None = None, + **_ # <- Allows other models to accept other keyword arguments + ): + thrust_coefficient = SimpleDeratingTurbine.thrust_coefficient( + power_thrust_table=power_thrust_table, + velocities=velocities, + air_density=air_density, + power_setpoints=power_setpoints, + average_method=average_method, + cubature_weights=cubature_weights, + ) + + return (1 - np.sqrt(1 - thrust_coefficient))/2 + +@define +class MixedOperationTurbine(BaseOperationModel): + + def power( + yaw_angles: NDArrayFloat, + power_setpoints: NDArrayFloat, + **kwargs + ): + yaw_angles_mask = yaw_angles > 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( + yaw_angles=yaw_angles, + **kwargs + )[yaw_angles_mask] + powers[power_setpoints_mask] += SimpleDeratingTurbine.power( + power_setpoints=power_setpoints, + **kwargs + )[power_setpoints_mask] + powers[neither_mask] += SimpleTurbine.power( + **kwargs + )[neither_mask] + + return powers + + def thrust_coefficient( + yaw_angles: NDArrayFloat, + power_setpoints: NDArrayFloat, + **kwargs + ): + yaw_angles_mask = yaw_angles > 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( + yaw_angles=yaw_angles, + **kwargs + )[yaw_angles_mask] + thrust_coefficients[power_setpoints_mask] += SimpleDeratingTurbine.thrust_coefficient( + power_setpoints=power_setpoints, + **kwargs + )[power_setpoints_mask] + thrust_coefficients[neither_mask] += SimpleTurbine.thrust_coefficient( + **kwargs + )[neither_mask] + + return thrust_coefficients + + def axial_induction( + yaw_angles: NDArrayFloat, + power_setpoints: NDArrayFloat, + **kwargs + ): + yaw_angles_mask = yaw_angles > 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( + yaw_angles=yaw_angles, + **kwargs + )[yaw_angles_mask] + axial_inductions[power_setpoints_mask] += SimpleDeratingTurbine.axial_induction( + power_setpoints=power_setpoints, + **kwargs + )[power_setpoints_mask] + axial_inductions[neither_mask] += SimpleTurbine.axial_induction( + **kwargs + )[neither_mask] + + return axial_inductions diff --git a/floris/simulation/turbine/turbine.py b/floris/simulation/turbine/turbine.py index d9aa76999..f9435facb 100644 --- a/floris/simulation/turbine/turbine.py +++ b/floris/simulation/turbine/turbine.py @@ -27,6 +27,8 @@ from floris.simulation import BaseClass from floris.simulation.turbine import ( CosineLossTurbine, + MixedOperationTurbine, + SimpleDeratingTurbine, SimpleTurbine, ) from floris.type_dec import ( @@ -44,7 +46,9 @@ TURBINE_MODEL_MAP = { "power_thrust_model": { "simple": SimpleTurbine, - "cosine-loss": CosineLossTurbine + "cosine-loss": CosineLossTurbine, + "simple-derating": SimpleDeratingTurbine, + "mixed": MixedOperationTurbine, }, } @@ -83,6 +87,7 @@ def power( power_functions: dict[str, Callable], yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, + power_setpoints: NDArrayFloat, tilt_interps: dict[str, interp1d], turbine_type_map: NDArrayObject, turbine_power_thrust_tables: dict, @@ -103,6 +108,8 @@ def power( each turbine type. Keys are the turbine type and values are the callable functions. yaw_angles (NDArrayFloat[findex, turbines]): The yaw angle for each turbine. tilt_angles (NDArrayFloat[findex, turbines]): The tilt angle for each turbine. + power_setpoints: (NDArrayFloat[findex, turbines]): Maximum power setpoint for each + turbine [W]. tilt_interps (Iterable[tuple]): The tilt interpolation functions for each turbine. turbine_type_map: (NDArrayObject[wd, ws, turbines]): The Turbine type definition for @@ -136,6 +143,7 @@ def power( velocities = velocities[:, ix_filter] yaw_angles = yaw_angles[:, ix_filter] tilt_angles = tilt_angles[:, ix_filter] + power_setpoints = power_setpoints[:, ix_filter] turbine_type_map = turbine_type_map[:, ix_filter] if type(correct_cp_ct_for_tilt) is bool: pass @@ -165,6 +173,7 @@ def power( "air_density": air_density, "yaw_angles": yaw_angles, "tilt_angles": tilt_angles, + "power_setpoints": power_setpoints, "tilt_interp": tilt_interps[turb_type], "average_method": average_method, "cubature_weights": cubature_weights, @@ -180,8 +189,10 @@ def power( def thrust_coefficient( velocities: NDArrayFloat, + air_density: float, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, + power_setpoints: NDArrayFloat, thrust_coefficient_functions: dict[str, Callable], tilt_interps: dict[str, interp1d], correct_cp_ct_for_tilt: NDArrayBool, @@ -200,8 +211,11 @@ def thrust_coefficient( Args: velocities (NDArrayFloat[findex, turbines, grid1, grid2]): The velocity field at a turbine. + air_density (float): air density for simulation [kg/m^3] yaw_angles (NDArrayFloat[findex, turbines]): The yaw angle for each turbine. tilt_angles (NDArrayFloat[findex, turbines]): The tilt angle for each turbine. + power_setpoints: (NDArrayFloat[findex, turbines]): Maximum power setpoint for each + turbine [W]. thrust_coefficient_functions (dict): The thrust coefficient functions for each turbine. Keys are the turbine type string and values are the callable functions. tilt_interps (Iterable[tuple]): The tilt interpolation functions for each @@ -230,6 +244,7 @@ def thrust_coefficient( velocities = velocities[:, ix_filter] yaw_angles = yaw_angles[:, ix_filter] tilt_angles = tilt_angles[:, ix_filter] + power_setpoints = power_setpoints[:, ix_filter] turbine_type_map = turbine_type_map[:, ix_filter] if type(correct_cp_ct_for_tilt) is bool: pass @@ -256,8 +271,10 @@ def thrust_coefficient( thrust_model_kwargs = { "power_thrust_table": power_thrust_table, "velocities": velocities, + "air_density": air_density, "yaw_angles": yaw_angles, "tilt_angles": tilt_angles, + "power_setpoints": power_setpoints, "tilt_interp": tilt_interps[turb_type], "average_method": average_method, "cubature_weights": cubature_weights, @@ -276,8 +293,10 @@ def thrust_coefficient( def axial_induction( velocities: NDArrayFloat, + air_density: float, yaw_angles: NDArrayFloat, tilt_angles: NDArrayFloat, + power_setpoints: NDArrayFloat, axial_induction_functions: dict, tilt_interps: NDArrayObject, correct_cp_ct_for_tilt: NDArrayBool, @@ -296,6 +315,8 @@ def axial_induction( (number of turbines, ngrid, ngrid), or (ngrid, ngrid) for a single turbine. yaw_angles (NDArrayFloat[findex, turbines]): The yaw angle for each turbine. tilt_angles (NDArrayFloat[findex, turbines]): The tilt angle for each turbine. + power_setpoints: (NDArrayFloat[findex, turbines]): Maximum power setpoint for each + turbine [W]. axial_induction_functions (dict): The axial induction functions for each turbine. Keys are the turbine type string and values are the callable functions. tilt_interps (Iterable[tuple]): The tilt interpolation functions for each @@ -324,6 +345,7 @@ def axial_induction( velocities = velocities[:, ix_filter] yaw_angles = yaw_angles[:, ix_filter] tilt_angles = tilt_angles[:, ix_filter] + power_setpoints = power_setpoints[:, ix_filter] turbine_type_map = turbine_type_map[:, ix_filter] if type(correct_cp_ct_for_tilt) is bool: pass @@ -350,8 +372,10 @@ def axial_induction( axial_induction_model_kwargs = { "power_thrust_table": power_thrust_table, "velocities": velocities, + "air_density": air_density, "yaw_angles": yaw_angles, "tilt_angles": tilt_angles, + "power_setpoints": power_setpoints, "tilt_interp": tilt_interps[turb_type], "average_method": average_method, "cubature_weights": cubature_weights, diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index f94bd13bb..1134c7842 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -23,6 +23,7 @@ from floris.logging_manager import LoggingManager from floris.simulation import Floris, State from floris.simulation.rotor_velocity import average_velocity +from floris.simulation.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.simulation.turbine.turbine import ( axial_induction, power, @@ -30,7 +31,7 @@ ) from floris.tools.cut_plane import CutPlane from floris.tools.wind_data import WindDataBase -from floris.type_dec import NDArrayFloat +from floris.type_dec import floris_array_converter, NDArrayFloat class FlorisInterface(LoggingManager): @@ -120,6 +121,7 @@ def calculate_wake( self, yaw_angles: NDArrayFloat | list[float] | None = None, # tilt_angles: NDArrayFloat | list[float] | None = None, + power_setpoints: NDArrayFloat | list[float] | list[float, None] | None = None, ) -> None: """ Wrapper to the :py:meth:`~.Farm.set_yaw_angles` and @@ -128,6 +130,9 @@ def calculate_wake( Args: yaw_angles (NDArrayFloat | list[float] | None, optional): Turbine yaw angles. Defaults to None. + power_setpoints (NDArrayFloat | list[float] | None, optional): Turbine power setpoints. + May be specified with some float values and some None values; power maximization + will be assumed for any None value. Defaults to None. """ if yaw_angles is None: @@ -139,6 +144,24 @@ def calculate_wake( ) self.floris.farm.yaw_angles = yaw_angles + if power_setpoints is None: + power_setpoints = POWER_SETPOINT_DEFAULT * np.ones( + ( + self.floris.flow_field.n_findex, + self.floris.farm.n_turbines, + ) + ) + else: + power_setpoints = np.array(power_setpoints) + + # Convert any None values to the default power setpoint + power_setpoints[ + power_setpoints == np.full(power_setpoints.shape, None) + ] = POWER_SETPOINT_DEFAULT + power_setpoints = floris_array_converter(power_setpoints) + + self.floris.farm.power_setpoints = power_setpoints + # # TODO is this required? # if tilt_angles is not None: # self.floris.farm.tilt_angles = tilt_angles @@ -651,6 +674,7 @@ def get_turbine_powers(self) -> NDArrayFloat: power_functions=self.floris.farm.turbine_power_functions, yaw_angles=self.floris.farm.yaw_angles, tilt_angles=self.floris.farm.tilt_angles, + power_setpoints=self.floris.farm.power_setpoints, tilt_interps=self.floris.farm.turbine_tilt_interps, turbine_type_map=self.floris.farm.turbine_type_map, turbine_power_thrust_tables=self.floris.farm.turbine_power_thrust_tables, @@ -662,8 +686,10 @@ def get_turbine_powers(self) -> NDArrayFloat: def get_turbine_thrust_coefficients(self) -> NDArrayFloat: turbine_thrust_coefficients = thrust_coefficient( velocities=self.floris.flow_field.u, + air_density=self.floris.flow_field.air_density, yaw_angles=self.floris.farm.yaw_angles, tilt_angles=self.floris.farm.tilt_angles, + power_setpoints=self.floris.farm.power_setpoints, thrust_coefficient_functions=self.floris.farm.turbine_thrust_coefficient_functions, tilt_interps=self.floris.farm.turbine_tilt_interps, correct_cp_ct_for_tilt=self.floris.farm.correct_cp_ct_for_tilt, @@ -678,8 +704,10 @@ def get_turbine_thrust_coefficients(self) -> NDArrayFloat: def get_turbine_ais(self) -> NDArrayFloat: turbine_ais = axial_induction( velocities=self.floris.flow_field.u, + air_density=self.floris.flow_field.air_density, yaw_angles=self.floris.farm.yaw_angles, tilt_angles=self.floris.farm.tilt_angles, + power_setpoints=self.floris.farm.power_setpoints, axial_induction_functions=self.floris.farm.turbine_axial_induction_functions, tilt_interps=self.floris.farm.turbine_tilt_interps, correct_cp_ct_for_tilt=self.floris.farm.correct_cp_ct_for_tilt, diff --git a/tests/farm_unit_test.py b/tests/farm_unit_test.py index 8fa9b28b5..72394c76b 100644 --- a/tests/farm_unit_test.py +++ b/tests/farm_unit_test.py @@ -62,6 +62,7 @@ def test_asdict(sample_inputs_fixture: SampleInputs): farm.construct_turbine_ref_tilts() farm.set_yaw_angles(N_FINDEX) farm.set_tilt_to_ref_tilt(N_FINDEX) + farm.set_power_setpoints(N_FINDEX) dict1 = farm.as_dict() new_farm = farm.from_dict(dict1) @@ -69,6 +70,7 @@ def test_asdict(sample_inputs_fixture: SampleInputs): new_farm.construct_turbine_ref_tilts() new_farm.set_yaw_angles(N_FINDEX) new_farm.set_tilt_to_ref_tilt(N_FINDEX) + new_farm.set_power_setpoints(N_FINDEX) dict2 = new_farm.as_dict() assert dict1 == dict2 diff --git a/tests/floris_interface_test.py b/tests/floris_interface_test.py index 17d612a38..d95e3b081 100644 --- a/tests/floris_interface_test.py +++ b/tests/floris_interface_test.py @@ -3,6 +3,7 @@ import numpy as np import pytest +from floris.simulation.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.tools.floris_interface import FlorisInterface @@ -30,6 +31,22 @@ def test_calculate_wake(): fi.calculate_wake(yaw_angles=yaw_angles) assert fi.floris.farm.yaw_angles == yaw_angles + power_setpoints = 1e6*np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) + fi.calculate_wake(power_setpoints=power_setpoints) + assert fi.floris.farm.power_setpoints == power_setpoints + + fi.calculate_wake(power_setpoints=None) + assert fi.floris.farm.power_setpoints == ( + POWER_SETPOINT_DEFAULT * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) + ) + + fi.reinitialize(layout_x=[0, 0], layout_y=[0, 1000]) + power_setpoints = np.array([[1e6, None]]) + fi.calculate_wake(power_setpoints=power_setpoints) + assert np.allclose( + fi.floris.farm.power_setpoints, + np.array([[power_setpoints[0, 0], POWER_SETPOINT_DEFAULT]]) + ) def test_calculate_no_wake(): """ diff --git a/tests/reg_tests/cumulative_curl_regression_test.py b/tests/reg_tests/cumulative_curl_regression_test.py index 531224656..bb28909b9 100644 --- a/tests/reg_tests/cumulative_curl_regression_test.py +++ b/tests/reg_tests/cumulative_curl_regression_test.py @@ -171,8 +171,10 @@ def test_regression_tandem(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -180,8 +182,10 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -190,18 +194,21 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -325,8 +332,10 @@ def test_regression_yaw(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -334,8 +343,10 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -344,18 +355,21 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -407,8 +421,10 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -416,8 +432,10 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -426,18 +444,21 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -488,8 +509,10 @@ def test_regression_secondary_steering(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -497,8 +520,10 @@ def test_regression_secondary_steering(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -507,18 +532,21 @@ def test_regression_secondary_steering(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -583,15 +611,18 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u + 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 farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, diff --git a/tests/reg_tests/empirical_gauss_regression_test.py b/tests/reg_tests/empirical_gauss_regression_test.py index d91dc956d..941d36063 100644 --- a/tests/reg_tests/empirical_gauss_regression_test.py +++ b/tests/reg_tests/empirical_gauss_regression_test.py @@ -144,8 +144,10 @@ def test_regression_tandem(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -153,8 +155,10 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -163,18 +167,21 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, - floris.farm.yaw_angles, - floris.farm.tilt_angles, + yaw_angles, + tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -300,8 +307,10 @@ def test_regression_yaw(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -309,8 +318,10 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -319,18 +330,21 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, - floris.farm.yaw_angles, - floris.farm.tilt_angles, + yaw_angles, + tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -382,8 +396,10 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -391,8 +407,10 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -401,18 +419,21 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, - floris.farm.yaw_angles, - floris.farm.tilt_angles, + yaw_angles, + tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -447,8 +468,10 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -456,8 +479,10 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -466,18 +491,21 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, - floris.farm.yaw_angles, - floris.farm.tilt_angles, + yaw_angles, + tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -563,6 +591,7 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): floris.farm.turbine_power_functions, floris.farm.yaw_angles, floris.farm.tilt_angles, + floris.farm.power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, diff --git a/tests/reg_tests/floris_interface_regression_test.py b/tests/reg_tests/floris_interface_regression_test.py index 7c9a0d2ff..9110ade8b 100644 --- a/tests/reg_tests/floris_interface_regression_test.py +++ b/tests/reg_tests/floris_interface_regression_test.py @@ -84,8 +84,10 @@ def test_calculate_no_wake(sample_inputs_fixture): n_findex = fi.floris.flow_field.n_findex velocities = fi.floris.flow_field.u + air_density = fi.floris.flow_field.air_density yaw_angles = fi.floris.farm.yaw_angles tilt_angles = fi.floris.farm.tilt_angles + power_setpoints = fi.floris.farm.power_setpoints test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -93,8 +95,10 @@ def test_calculate_no_wake(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, fi.floris.farm.turbine_thrust_coefficient_functions, fi.floris.farm.turbine_tilt_interps, fi.floris.farm.correct_cp_ct_for_tilt, @@ -103,18 +107,21 @@ def test_calculate_no_wake(sample_inputs_fixture): ) farm_powers = power( velocities, - fi.floris.flow_field.air_density, + air_density, fi.floris.farm.turbine_power_functions, fi.floris.farm.yaw_angles, fi.floris.farm.tilt_angles, + fi.floris.farm.power_setpoints, fi.floris.farm.turbine_tilt_interps, fi.floris.farm.turbine_type_map, fi.floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, fi.floris.farm.turbine_axial_induction_functions, fi.floris.farm.turbine_tilt_interps, fi.floris.farm.correct_cp_ct_for_tilt, diff --git a/tests/reg_tests/gauss_regression_test.py b/tests/reg_tests/gauss_regression_test.py index 3a3fa4777..f04ce106e 100644 --- a/tests/reg_tests/gauss_regression_test.py +++ b/tests/reg_tests/gauss_regression_test.py @@ -262,8 +262,10 @@ def test_regression_tandem(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -271,8 +273,10 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -281,18 +285,21 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -416,8 +423,10 @@ def test_regression_yaw(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -425,8 +434,10 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -435,18 +446,21 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -495,8 +509,10 @@ def test_regression_gch(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -504,8 +520,10 @@ def test_regression_gch(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -514,18 +532,21 @@ def test_regression_gch(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -569,8 +590,10 @@ def test_regression_gch(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -578,8 +601,10 @@ def test_regression_gch(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -588,18 +613,21 @@ def test_regression_gch(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -651,8 +679,10 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -660,8 +690,10 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -670,18 +702,21 @@ def test_regression_yaw_added_recovery(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -732,8 +767,10 @@ def test_regression_secondary_steering(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -741,8 +778,10 @@ def test_regression_secondary_steering(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -751,18 +790,21 @@ def test_regression_secondary_steering(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -829,6 +871,7 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): velocities = floris.flow_field.u yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles + power_setpoints = floris.farm.power_setpoints farm_powers = power( velocities, @@ -836,6 +879,7 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, diff --git a/tests/reg_tests/jensen_jimenez_regression_test.py b/tests/reg_tests/jensen_jimenez_regression_test.py index f54ddda6a..ad1862570 100644 --- a/tests/reg_tests/jensen_jimenez_regression_test.py +++ b/tests/reg_tests/jensen_jimenez_regression_test.py @@ -113,8 +113,10 @@ def test_regression_tandem(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -122,8 +124,10 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -132,18 +136,21 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, - floris.farm.yaw_angles, - floris.farm.tilt_angles, + yaw_angles, + tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -267,8 +274,10 @@ def test_regression_yaw(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -276,8 +285,10 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -286,18 +297,21 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -362,8 +376,10 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u + 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 # farm_eff_velocities = rotor_effective_velocity( # floris.flow_field.air_density, @@ -380,10 +396,11 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): # ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, diff --git a/tests/reg_tests/none_regression_test.py b/tests/reg_tests/none_regression_test.py index 5fd2c99ac..e8f42c8cc 100644 --- a/tests/reg_tests/none_regression_test.py +++ b/tests/reg_tests/none_regression_test.py @@ -114,8 +114,10 @@ def test_regression_tandem(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -123,8 +125,10 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -133,18 +137,21 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -305,15 +312,18 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): # farm_avg_velocities = average_velocity(floris.flow_field.u) velocities = floris.flow_field.u + 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 farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, diff --git a/tests/reg_tests/turbopark_regression_test.py b/tests/reg_tests/turbopark_regression_test.py index 39dffcd78..eaba6fadc 100644 --- a/tests/reg_tests/turbopark_regression_test.py +++ b/tests/reg_tests/turbopark_regression_test.py @@ -115,8 +115,10 @@ def test_regression_tandem(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -124,8 +126,10 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -134,18 +138,21 @@ def test_regression_tandem(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -270,8 +277,10 @@ def test_regression_yaw(sample_inputs_fixture): n_findex = floris.flow_field.n_findex velocities = floris.flow_field.u + 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 test_results = np.zeros((n_findex, n_turbines, 4)) farm_avg_velocities = average_velocity( @@ -279,8 +288,10 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_cts = thrust_coefficient( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_thrust_coefficient_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -289,18 +300,21 @@ def test_regression_yaw(sample_inputs_fixture): ) farm_powers = power( velocities, - floris.flow_field.air_density, + air_density, floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, ) farm_axial_inductions = axial_induction( velocities, + air_density, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_axial_induction_functions, floris.farm.turbine_tilt_interps, floris.farm.correct_cp_ct_for_tilt, @@ -362,6 +376,7 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): velocities = floris.flow_field.u yaw_angles = floris.farm.yaw_angles tilt_angles = floris.farm.tilt_angles + power_setpoints = floris.farm.power_setpoints farm_powers = power( velocities, @@ -369,6 +384,7 @@ def test_regression_small_grid_rotation(sample_inputs_fixture): floris.farm.turbine_power_functions, yaw_angles, tilt_angles, + power_setpoints, floris.farm.turbine_tilt_interps, floris.farm.turbine_type_map, floris.farm.turbine_power_thrust_tables, diff --git a/tests/turbine_multi_dim_unit_test.py b/tests/turbine_multi_dim_unit_test.py index 2d8635539..cc4b9f7ed 100644 --- a/tests/turbine_multi_dim_unit_test.py +++ b/tests/turbine_multi_dim_unit_test.py @@ -22,6 +22,7 @@ from floris.simulation import ( Turbine, ) +from floris.simulation.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.simulation.turbine.turbine import ( axial_induction, power, @@ -96,8 +97,10 @@ def test_ct(): wind_speed = 10.0 thrust = thrust_coefficient( velocities=wind_speed * np.ones((1, 1, 3, 3)), + air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), @@ -112,8 +115,10 @@ def test_ct(): # 4 turbines with 3 x 3 grid arrays thrusts = thrust_coefficient( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 16 x 4 x 3 x 3 + air_density=None, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, + power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), @@ -166,6 +171,7 @@ def test_power(): power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, 1)), # 1 findex, 1 turbine tilt_angles=turbine.power_thrust_table[condition]["ref_tilt"] * np.ones((1, 1)), + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, @@ -184,6 +190,7 @@ def test_power(): power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, + power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, ix_filter=INDEX_FILTER, @@ -220,8 +227,10 @@ def test_axial_induction(): wind_speed = 10.0 ai = axial_induction( velocities=wind_speed * np.ones((1, 1, 3, 3)), + air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, + power_setpoints = np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), @@ -234,8 +243,10 @@ def test_axial_induction(): # Multiple turbines with ix filter ai = axial_induction( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 16 x 4 x 3 x 3 + air_density=None, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, + power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), diff --git a/tests/turbine_operation_models_test.py b/tests/turbine_operation_models_test.py index 517bb0be7..446695855 100644 --- a/tests/turbine_operation_models_test.py +++ b/tests/turbine_operation_models_test.py @@ -1,8 +1,12 @@ import numpy as np +import pytest from floris.simulation.turbine.operation_models import ( CosineLossTurbine, + MixedOperationTurbine, + POWER_SETPOINT_DEFAULT, rotor_velocity_air_density_correction, + SimpleDeratingTurbine, SimpleTurbine, ) from floris.utilities import cosd @@ -31,9 +35,19 @@ def test_submodel_attributes(): assert hasattr(SimpleTurbine, "power") assert hasattr(SimpleTurbine, "thrust_coefficient") + assert hasattr(SimpleTurbine, "axial_induction") assert hasattr(CosineLossTurbine, "power") assert hasattr(CosineLossTurbine, "thrust_coefficient") + assert hasattr(CosineLossTurbine, "axial_induction") + + assert hasattr(SimpleDeratingTurbine, "power") + assert hasattr(SimpleDeratingTurbine, "thrust_coefficient") + assert hasattr(SimpleDeratingTurbine, "axial_induction") + + assert hasattr(MixedOperationTurbine, "power") + assert hasattr(MixedOperationTurbine, "thrust_coefficient") + assert hasattr(MixedOperationTurbine, "axial_induction") def test_SimpleTurbine(): @@ -213,3 +227,274 @@ def test_CosineLossTurbine(): ) absolute_tilt = tilt_angles_test - turbine_data["power_thrust_table"]["ref_tilt"] assert test_Ct == baseline_Ct * cosd(yaw_angles_test) * cosd(absolute_tilt) + +def test_SimpleDeratingTurbine(): + + n_turbines = 1 + wind_speed = 10.0 + turbine_data = SampleInputs().turbine + + # Check that for no specified derating, matches SimpleTurbine + test_Ct = SimpleDeratingTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=None, + ) + base_Ct = SimpleTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + ) + assert np.allclose(test_Ct, base_Ct) + + test_power = SimpleDeratingTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=None, + ) + base_power = SimpleTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + ) + assert np.allclose(test_power, base_power) + + test_ai = SimpleDeratingTurbine.axial_induction( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=None, + ) + base_ai = SimpleTurbine.axial_induction( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + ) + assert np.allclose(test_ai, base_ai) + + # When power_setpoints are 0, turbine is shut down. + test_Ct = SimpleDeratingTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.zeros((1, n_turbines)), + ) + assert np.allclose(test_Ct, 0) + + test_power = SimpleDeratingTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.zeros((1, n_turbines)), + ) + assert np.allclose(test_power, 0) + + test_ai = SimpleDeratingTurbine.axial_induction( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.zeros((1, n_turbines)), + ) + assert np.allclose(test_ai, 0) + + # When power setpoints are less than available, results should be less than when no setpoint + wind_speed = 20 # High, so that turbine is above rated nominally + derated_power = 4.0e6 + rated_power = 5.0e6 + test_power = SimpleDeratingTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=derated_power * np.ones((1, n_turbines)), + ) + + rated_power = 5.0e6 + test_Ct = SimpleDeratingTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=derated_power * np.ones((1, n_turbines)), + ) + base_Ct = SimpleTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=derated_power * np.ones((1, n_turbines)), + ) + assert np.allclose(test_Ct, derated_power/rated_power * base_Ct) # Is this correct? + + # Mixed below and above rated + n_turbines = 2 + wind_speeds_test = np.ones((1, n_turbines, 3, 3)) + wind_speeds_test[0,0,:,:] = 20.0 # Above rated + wind_speeds_test[0,1,:,:] = 5.0 # Well below eated + test_power = SimpleDeratingTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speeds_test, # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=derated_power * np.ones((1, n_turbines)), + ) + base_power = SimpleTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speeds_test, # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=derated_power * np.ones((1, n_turbines)), + ) + + assert test_power[0,0] < base_power[0,0] + assert test_power[0,0] == derated_power + + assert test_power[0,1] == base_power[0,1] + assert test_power[0,1] < derated_power + +def test_MixedOperationTurbine(): + + n_turbines = 1 + wind_speed = 10.0 + turbine_data = SampleInputs().turbine + tilt_angles_nom = turbine_data["power_thrust_table"]["ref_tilt"] * np.ones((1, n_turbines)) + + # Check that for no specified derating or yaw angle, matches SimpleTurbine + test_Ct = MixedOperationTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((1, n_turbines)), + yaw_angles=np.zeros((1, n_turbines)), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_Ct = SimpleTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + ) + + assert np.allclose(test_Ct, base_Ct) + + test_power = MixedOperationTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((1, n_turbines)), + yaw_angles=np.zeros((1, n_turbines)), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_power = SimpleTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + ) + assert np.allclose(test_power, base_power) + + test_ai = MixedOperationTurbine.axial_induction( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=POWER_SETPOINT_DEFAULT * np.ones((1, n_turbines)), + yaw_angles=np.zeros((1, n_turbines)), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_ai = SimpleTurbine.axial_induction( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + ) + assert np.allclose(test_ai, base_ai) + + # Check that when power_setpoints are set, matches SimpleDeratingTurbine, + # while when yaw angles are set, matches CosineLossTurbine + n_turbines = 2 + derated_power = 2.0e6 + + test_Ct = MixedOperationTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), + yaw_angles=np.array([[20.0, 0.0]]), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_Ct_dr = SimpleDeratingTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), + ) + base_Ct_yaw = CosineLossTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + yaw_angles=np.array([[20.0, 0.0]]), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_Ct = np.array([[base_Ct_yaw[0,0], base_Ct_dr[0,1]]]) + assert np.allclose(test_Ct, base_Ct) + + # Do the same as above for power() + test_power = MixedOperationTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), + yaw_angles=np.array([[20.0, 0.0]]), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_power_dr = SimpleDeratingTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), + ) + base_power_yaw = CosineLossTurbine.power( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + yaw_angles=np.array([[20.0, 0.0]]), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_power = np.array([[base_power_yaw[0,0], base_power_dr[0,1]]]) + assert np.allclose(test_power, base_power) + + # Finally, check axial induction + test_ai = MixedOperationTurbine.axial_induction( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), + yaw_angles=np.array([[20.0, 0.0]]), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_ai_dr = SimpleDeratingTurbine.axial_induction( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), + ) + base_ai_yaw = CosineLossTurbine.axial_induction( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + yaw_angles=np.array([[20.0, 0.0]]), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) + base_ai = np.array([[base_ai_yaw[0,0], base_ai_dr[0,1]]]) + assert np.allclose(test_ai, base_ai) + + # Check error raised when both yaw and power setpoints are set + with pytest.raises(ValueError): + # Second turbine has both a power setpoint and a yaw angle + MixedOperationTurbine.thrust_coefficient( + power_thrust_table=turbine_data["power_thrust_table"], + velocities=wind_speed * np.ones((1, n_turbines, 3, 3)), # 1 findex, 1 turbine, 3x3 grid + air_density=turbine_data["power_thrust_table"]["ref_air_density"], + power_setpoints=np.array([[POWER_SETPOINT_DEFAULT, derated_power]]), + yaw_angles=np.array([[0.0, 20.0]]), + tilt_angles=tilt_angles_nom, + tilt_interp=None + ) diff --git a/tests/turbine_unit_test.py b/tests/turbine_unit_test.py index 8941d3163..5b95d9dde 100644 --- a/tests/turbine_unit_test.py +++ b/tests/turbine_unit_test.py @@ -28,6 +28,7 @@ thrust_coefficient, Turbine, ) +from floris.simulation.turbine.operation_models import POWER_SETPOINT_DEFAULT from tests.conftest import SampleInputs, WIND_SPEEDS @@ -187,8 +188,10 @@ def test_ct(): wind_speed = 10.0 thrust = thrust_coefficient( velocities=wind_speed * np.ones((1, 1, 3, 3)), + air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), @@ -206,8 +209,10 @@ def test_ct(): # 4 turbines with 3 x 3 grid arrays thrusts = thrust_coefficient( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 12 x 4 x 3 x 3 + air_density=None, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, + power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, thrust_coefficient_functions={turbine.turbine_type: turbine.thrust_coefficient_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), @@ -227,8 +232,10 @@ def test_ct(): # Single floating turbine; note that 'tilt_interp' is not set to None thrust = thrust_coefficient( velocities=wind_speed * np.ones((1, 1, 3, 3)), # One findex, one turbine + air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, thrust_coefficient_functions={ turbine.turbine_type: turbine_floating.thrust_coefficient_function }, @@ -260,6 +267,7 @@ def test_power(): air_density=turbine.power_thrust_table["ref_air_density"], power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, 1)), # 1 findex, 1 turbine + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, 1)), tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map[:,0], @@ -280,6 +288,7 @@ def test_power(): power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, 1)), # 1 findex, 1 turbine tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, 1)), + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, @@ -295,6 +304,7 @@ def test_power(): power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, 1)), # 1 findex, 1 turbine tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, 1)), + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map[:,0], turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, @@ -315,6 +325,7 @@ def test_power(): power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, n_turbines)), tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, n_turbines)), + power_setpoints=np.ones((1, n_turbines)) * POWER_SETPOINT_DEFAULT, tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, @@ -335,6 +346,7 @@ def test_power(): power_functions={turbine.turbine_type: turbine.power_function}, yaw_angles=np.zeros((1, n_turbines)), tilt_angles=turbine.power_thrust_table["ref_tilt"] * np.ones((1, n_turbines)), + power_setpoints=np.ones((1, n_turbines)) * POWER_SETPOINT_DEFAULT, tilt_interps={turbine.turbine_type: turbine.tilt_interp}, turbine_type_map=turbine_type_map, turbine_power_thrust_tables={turbine.turbine_type: turbine.power_thrust_table}, @@ -361,8 +373,10 @@ def test_axial_induction(): wind_speed = 10.0 ai = axial_induction( velocities=wind_speed * np.ones((1, 1, 3, 3)), # 1 findex, 1 Turbine + air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False]]), @@ -374,8 +388,10 @@ def test_axial_induction(): # Multiple turbines with ix filter ai = axial_induction( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 12 x 4 x 3 x 3 + air_density=None, yaw_angles=np.zeros((1, N_TURBINES)), tilt_angles=np.ones((1, N_TURBINES)) * 5.0, + power_setpoints=np.ones((1, N_TURBINES)) * POWER_SETPOINT_DEFAULT, axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine.turbine_type: None}, correct_cp_ct_for_tilt=np.array([[False] * N_TURBINES]), @@ -392,8 +408,10 @@ def test_axial_induction(): # Single floating turbine; note that 'tilt_interp' is not set to None ai = axial_induction( velocities=wind_speed * np.ones((1, 1, 3, 3)), + air_density=None, yaw_angles=np.zeros((1, 1)), tilt_angles=np.ones((1, 1)) * 5.0, + power_setpoints=np.ones((1, 1)) * POWER_SETPOINT_DEFAULT, axial_induction_functions={turbine.turbine_type: turbine.axial_induction_function}, tilt_interps={turbine_floating.turbine_type: turbine_floating.tilt_interp}, correct_cp_ct_for_tilt=np.array([[True]]),