From 8d3b0ed1ee127b60920c8a47bd9f192601dae12c Mon Sep 17 00:00:00 2001 From: misi9170 Date: Thu, 6 Jul 2023 12:53:50 -0600 Subject: [PATCH 1/6] Allow power interpolation values outside of range (set to zero). --- floris/simulation/turbine.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/floris/simulation/turbine.py b/floris/simulation/turbine.py index b1cef9858..f0e9d732f 100644 --- a/floris/simulation/turbine.py +++ b/floris/simulation/turbine.py @@ -265,6 +265,8 @@ def power( for turb_type in turb_types: # Using a masked array, apply the thrust coefficient for all turbines of the current # type to the main thrust coefficient array + if (rotor_effective_velocities < 0.).any(): + print("Some rotor effective velocities are negative.") p += ( power_interp[turb_type](rotor_effective_velocities) * (turbine_type_map == turb_type) @@ -665,7 +667,9 @@ def __attrs_post_init__(self) -> None: ) self.power_interp = interp1d( wind_speeds, - inner_power + inner_power, + bounds_error=False, + fill_value=0 ) """ From db04ade0fa2d97b04dbcb6a4e97ea591dd69d2a4 Mon Sep 17 00:00:00 2001 From: misi9170 Date: Thu, 6 Jul 2023 13:48:52 -0600 Subject: [PATCH 2/6] updates to base class, scipy class, and SR class to handle heterogeneous inflows during yaw optimization. --- .../yaw_optimization/yaw_optimization_base.py | 7 ++++++- .../yaw_optimization/yaw_optimizer_scipy.py | 11 ++++++++++- .../optimization/yaw_optimization/yaw_optimizer_sr.py | 7 +++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/floris/tools/optimization/yaw_optimization/yaw_optimization_base.py b/floris/tools/optimization/yaw_optimization/yaw_optimization_base.py index ddddff55a..6a50247d9 100644 --- a/floris/tools/optimization/yaw_optimization/yaw_optimization_base.py +++ b/floris/tools/optimization/yaw_optimization/yaw_optimization_base.py @@ -338,7 +338,9 @@ def _normalize_control_problem(self): / self._normalization_length ) - def _calculate_farm_power(self, yaw_angles=None, wd_array=None, turbine_weights=None): + def _calculate_farm_power(self, yaw_angles=None, wd_array=None, turbine_weights=None, + heterogeneous_speed_multipliers=None + ): """ Calculate the wind farm power production assuming the predefined probability distribution (self.unc_options/unc_pmf), with the @@ -358,6 +360,9 @@ def _calculate_farm_power(self, yaw_angles=None, wd_array=None, turbine_weights= yaw_angles = self._yaw_angles_baseline_subset if turbine_weights is None: turbine_weights = self._turbine_weights_subset + if heterogeneous_speed_multipliers is not None: + fi_subset.floris.flow_field.\ + heterogenous_inflow_config['speed_multipliers'] = heterogeneous_speed_multipliers # Ensure format [incompatible with _subset notation] yaw_angles = self._unpack_variable(yaw_angles, subset=True) diff --git a/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py b/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py index 81ffc32a3..aa387bd5d 100644 --- a/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py +++ b/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py @@ -108,6 +108,14 @@ def optimize(self): yaw_template = np.tile(yaw_template, (1, 1, 1)) turbine_weights = np.tile(turbine_weights, (1, 1, 1)) + # Handle heterogeneous inflow, if there is one + if (hasattr(self.fi.floris.flow_field, 'heterogenous_inflow_config') and + self.fi.floris.flow_field.heterogenous_inflow_config is not None): + het_sm_orig = np.array(self.fi.floris.flow_field.heterogenous_inflow_config['speed_multipliers']) + het_sm = het_sm_orig[nwdi,:].reshape(1,-1) + else: + het_sm = None + # Define cost function def cost(x): x_full = np.array(yaw_template, copy=True) @@ -116,7 +124,8 @@ def cost(x): - 1.0 * self._calculate_farm_power( yaw_angles=x_full, wd_array=[wd], - turbine_weights=turbine_weights + turbine_weights=turbine_weights, + heterogeneous_speed_multipliers=het_sm )[0, 0] / J0 ) diff --git a/floris/tools/optimization/yaw_optimization/yaw_optimizer_sr.py b/floris/tools/optimization/yaw_optimization/yaw_optimizer_sr.py index 801c59312..a46ed2d21 100644 --- a/floris/tools/optimization/yaw_optimization/yaw_optimizer_sr.py +++ b/floris/tools/optimization/yaw_optimization/yaw_optimizer_sr.py @@ -141,10 +141,17 @@ def _calc_powers_with_memory(self, yaw_angles_subset, use_memory=True): if not np.all(idx): # Now calculate farm powers for conditions we haven't yet evaluated previously start_time = timerpc() + if (hasattr(self.fi.floris.flow_field, 'heterogenous_inflow_config') and + self.fi.floris.flow_field.heterogenous_inflow_config is not None): + het_sm_orig = np.array(self.fi.floris.flow_field.heterogenous_inflow_config['speed_multipliers']) + het_sm = np.tile(het_sm_orig, (Ny, 1))[~idx, :] + else: + het_sm = None farm_powers[~idx, :] = self._calculate_farm_power( wd_array=wd_array_subset[~idx], turbine_weights=turbine_weights_subset[~idx, :, :], yaw_angles=yaw_angles_subset[~idx, :, :], + heterogeneous_speed_multipliers=het_sm ) self.time_spent_in_floris += (timerpc() - start_time) From 8cb0abc86dc09aa03197179373c2f68ecc0cb194 Mon Sep 17 00:00:00 2001 From: misi9170 Date: Thu, 6 Jul 2023 13:59:09 -0600 Subject: [PATCH 3/6] Formating w ruff. --- .../optimization/yaw_optimization/yaw_optimizer_scipy.py | 6 ++++-- .../tools/optimization/yaw_optimization/yaw_optimizer_sr.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py b/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py index aa387bd5d..66339e426 100644 --- a/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py +++ b/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py @@ -109,9 +109,11 @@ def optimize(self): turbine_weights = np.tile(turbine_weights, (1, 1, 1)) # Handle heterogeneous inflow, if there is one - if (hasattr(self.fi.floris.flow_field, 'heterogenous_inflow_config') and + if (hasattr(self.fi.floris.flow_field, 'heterogenous_inflow_config') and self.fi.floris.flow_field.heterogenous_inflow_config is not None): - het_sm_orig = np.array(self.fi.floris.flow_field.heterogenous_inflow_config['speed_multipliers']) + het_sm_orig = np.array( + self.fi.floris.flow_field.heterogenous_inflow_config['speed_multipliers'] + ) het_sm = het_sm_orig[nwdi,:].reshape(1,-1) else: het_sm = None diff --git a/floris/tools/optimization/yaw_optimization/yaw_optimizer_sr.py b/floris/tools/optimization/yaw_optimization/yaw_optimizer_sr.py index a46ed2d21..82d50ef08 100644 --- a/floris/tools/optimization/yaw_optimization/yaw_optimizer_sr.py +++ b/floris/tools/optimization/yaw_optimization/yaw_optimizer_sr.py @@ -141,9 +141,11 @@ def _calc_powers_with_memory(self, yaw_angles_subset, use_memory=True): if not np.all(idx): # Now calculate farm powers for conditions we haven't yet evaluated previously start_time = timerpc() - if (hasattr(self.fi.floris.flow_field, 'heterogenous_inflow_config') and + if (hasattr(self.fi.floris.flow_field, 'heterogenous_inflow_config') and self.fi.floris.flow_field.heterogenous_inflow_config is not None): - het_sm_orig = np.array(self.fi.floris.flow_field.heterogenous_inflow_config['speed_multipliers']) + het_sm_orig = np.array( + self.fi.floris.flow_field.heterogenous_inflow_config['speed_multipliers'] + ) het_sm = np.tile(het_sm_orig, (Ny, 1))[~idx, :] else: het_sm = None From 9c5d1994feb4a117ac86ff6f0bc9717e768ab5f5 Mon Sep 17 00:00:00 2001 From: misi9170 Date: Thu, 6 Jul 2023 16:48:42 -0600 Subject: [PATCH 4/6] Moving warning from power() to get_turbine_powers() on FlorisInterface. --- floris/simulation/turbine.py | 2 -- floris/tools/floris_interface.py | 4 ++++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/floris/simulation/turbine.py b/floris/simulation/turbine.py index f0e9d732f..b284b10cd 100644 --- a/floris/simulation/turbine.py +++ b/floris/simulation/turbine.py @@ -265,8 +265,6 @@ def power( for turb_type in turb_types: # Using a masked array, apply the thrust coefficient for all turbines of the current # type to the main thrust coefficient array - if (rotor_effective_velocities < 0.).any(): - print("Some rotor effective velocities are negative.") p += ( power_interp[turb_type](rotor_effective_velocities) * (turbine_type_map == turb_type) diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index 4e4cc352e..e4c913096 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -595,6 +595,10 @@ def get_turbine_powers(self) -> NDArrayFloat: "Can't run function `FlorisInterface.get_turbine_powers` without " "first running `FlorisInterface.calculate_wake`." ) + # Check for negative velocities, which could indicate bad model + # parameters or turbines very closely spaced. + if (self.turbine_effective_velocities < 0.).any(): + print("WARNING: Some rotor effective velocities are negative.") turbine_powers = power( ref_density_cp_ct=self.floris.farm.ref_density_cp_cts, From d0ee0ec118b50d516a43abd98832986d3250becb Mon Sep 17 00:00:00 2001 From: misi9170 Date: Mon, 10 Jul 2023 12:15:24 -0600 Subject: [PATCH 5/6] Use logging manager to handle warning. --- floris/tools/floris_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index e4c913096..a0d50e656 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -598,7 +598,7 @@ def get_turbine_powers(self) -> NDArrayFloat: # Check for negative velocities, which could indicate bad model # parameters or turbines very closely spaced. if (self.turbine_effective_velocities < 0.).any(): - print("WARNING: Some rotor effective velocities are negative.") + self.logger.warning("WARNING: Some rotor effective velocities are negative.") turbine_powers = power( ref_density_cp_ct=self.floris.farm.ref_density_cp_cts, From 829cda97f29cad28c2e5eb68d6ae550d8df0369c Mon Sep 17 00:00:00 2001 From: misi9170 Date: Mon, 10 Jul 2023 12:25:30 -0600 Subject: [PATCH 6/6] Removing redundant WARNING label. --- floris/tools/floris_interface.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index a0d50e656..a09003ec6 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -598,7 +598,7 @@ def get_turbine_powers(self) -> NDArrayFloat: # Check for negative velocities, which could indicate bad model # parameters or turbines very closely spaced. if (self.turbine_effective_velocities < 0.).any(): - self.logger.warning("WARNING: Some rotor effective velocities are negative.") + self.logger.warning("Some rotor effective velocities are negative.") turbine_powers = power( ref_density_cp_ct=self.floris.farm.ref_density_cp_cts,