Source code for floris.core.solver


from __future__ import annotations

import copy

import numpy as np

from floris.core import (
    axial_induction,
    Farm,
    FlowField,
    FlowFieldGrid,
    FlowFieldPlanarGrid,
    PointsGrid,
    thrust_coefficient,
    TurbineGrid,
)
from floris.core.rotor_velocity import average_velocity
from floris.core.wake import WakeModelManager
from floris.core.wake_deflection.empirical_gauss import yaw_added_wake_mixing
from floris.core.wake_deflection.gauss import (
    calculate_transverse_velocity,
    wake_added_yaw,
    yaw_added_turbulence_mixing,
)
from floris.core.wake_velocity.empirical_gauss import awc_added_wake_mixing
from floris.type_dec import NDArrayFloat
from floris.utilities import cosd


[docs] def calculate_area_overlap(wake_velocities, freestream_velocities, y_ngrid, z_ngrid): """ compute wake overlap based on the number of points that are not freestream velocity, i.e. affected by the wake """ # Count all of the rotor points with a negligible difference from freestream # count = np.sum(freestream_velocities - wake_velocities <= 0.05, axis=(3, 4)) # return (y_ngrid * z_ngrid - count) / (y_ngrid * z_ngrid) # return 1 - count / (y_ngrid * z_ngrid) # Find the points on the rotor grids with a difference from freestream of greater # than some tolerance. These are all the points in the wake. The ratio of # these points to the total points is the portion of wake overlap. return np.sum(freestream_velocities - wake_velocities > 0.05, axis=(3, 4)) / (y_ngrid * z_ngrid)
# @profile
[docs] def sequential_solver( farm: Farm, flow_field: FlowField, grid: TurbineGrid, model_manager: WakeModelManager ) -> None: # Algorithm # For each turbine, calculate its effect on every downstream turbine. # For the current turbine, we are calculating the deficit that it adds to downstream turbines. # Integrate this into the main data structure. # Move on to the next turbine. # <<interface>> deflection_model_args = model_manager.deflection_model.prepare_function(grid, flow_field) deficit_model_args = model_manager.velocity_model.prepare_function(grid, flow_field) # This is u_wake wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) # Expand input turbulence intensity to 4d for (n_turbines, grid, grid) turbine_turbulence_intensity = flow_field.turbulence_intensities[:, None, None, None] turbine_turbulence_intensity = np.repeat(turbine_turbulence_intensity, farm.n_turbines, axis=1) # Ambient turbulent intensity should be a copy of n_findex-long turbulence_intensity # with dimensions expanded for (n_turbines, grid, grid) ambient_turbulence_intensities = flow_field.turbulence_intensities.copy() ambient_turbulence_intensities = ambient_turbulence_intensities[:, None, None, None] # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(grid.n_turbines): # Get the current turbine quantities x_i = np.mean(grid.x_sorted[:, i:i+1], axis=(2, 3)) x_i = x_i[:, :, None, None] y_i = np.mean(grid.y_sorted[:, i:i+1], axis=(2, 3)) y_i = y_i[:, :, None, None] z_i = np.mean(grid.z_sorted[:, i:i+1], axis=(2, 3)) z_i = z_i[:, :, None, None] u_i = flow_field.u_sorted[:, i:i+1] v_i = flow_field.v_sorted[:, i:i+1] ct_i = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions ) # Since we are filtering for the i'th turbine in the thrust coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] turbulence_intensity_i = turbine_turbulence_intensity[:, i:i+1] yaw_angle_i = farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = farm.rotor_diameters_sorted[:, i:i+1, None, None] TSR_i = farm.TSRs_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: added_yaw = wake_added_yaw( u_i, v_i, flow_field.u_initial_sorted, grid.y_sorted[:, i:i+1] - y_i, grid.z_sorted[:, i:i+1], rotor_diameter_i, hub_height_i, ct_i, TSR_i, axial_induction_i, flow_field.wind_shear, ) effective_yaw_i += added_yaw # Model calculations # NOTE: exponential deflection_field = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, turbulence_intensity_i, ct_i, rotor_diameter_i, **deflection_model_args, ) if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( u_i, flow_field.u_initial_sorted, flow_field.dudz_initial_sorted, grid.x_sorted - x_i, grid.y_sorted - y_i, grid.z_sorted, rotor_diameter_i, hub_height_i, yaw_angle_i, ct_i, TSR_i, axial_induction_i, flow_field.wind_shear, ) if model_manager.enable_yaw_added_recovery: I_mixing = yaw_added_turbulence_mixing( u_i, turbulence_intensity_i, v_i, flow_field.w_sorted[:, i:i+1], v_wake[:, i:i+1], w_wake[:, i:i+1], ) gch_gain = 2 turbine_turbulence_intensity[:, i:i+1] = turbulence_intensity_i + gch_gain * I_mixing # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, axial_induction_i, deflection_field, yaw_angle_i, turbulence_intensity_i, ct_i, hub_height_i, rotor_diameter_i, **deficit_model_args, ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) wake_added_turbulence_intensity = model_manager.turbulence_model.function( ambient_turbulence_intensities, grid.x_sorted, x_i, rotor_diameter_i, axial_induction_i, ) # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = ( np.sum(velocity_deficit * flow_field.u_initial_sorted > 0.05, axis=(2, 3)) / (grid.grid_resolution * grid.grid_resolution) ) area_overlap = area_overlap[:, :, None, None] # Modify wake added turbulence by wake area overlap downstream_influence_length = 15 * rotor_diameter_i ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) * (grid.x_sorted > x_i) * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbine_turbulence_intensity = np.maximum( np.sqrt(ti_added**2 + ambient_turbulence_intensities**2), turbine_turbulence_intensity ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.turbulence_intensity_field_sorted = turbine_turbulence_intensity flow_field.turbulence_intensity_field_sorted_avg = np.mean( turbine_turbulence_intensity, axis=(2,3) )[:, :, None, None]
[docs] def full_flow_sequential_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid | FlowFieldPlanarGrid | PointsGrid, model_manager: WakeModelManager ) -> None: # Get the flow quantities and turbine performance turbine_grid_farm = copy.deepcopy(farm) turbine_grid_flow_field = copy.deepcopy(flow_field) turbine_grid_farm.construct_turbine_map() turbine_grid_farm.construct_turbine_thrust_coefficient_functions() turbine_grid_farm.construct_turbine_axial_induction_functions() turbine_grid_farm.construct_turbine_power_functions() turbine_grid_farm.construct_hub_heights() turbine_grid_farm.construct_rotor_diameters() turbine_grid_farm.construct_turbine_TSRs() turbine_grid_farm.construct_turbine_ref_tilts() turbine_grid_farm.construct_turbine_tilt_interps() turbine_grid_farm.construct_turbine_correct_cp_ct_for_tilt() turbine_grid_farm.set_tilt_to_ref_tilt(flow_field.n_findex) turbine_grid = TurbineGrid( turbine_coordinates=turbine_grid_farm.coordinates, turbine_diameters=turbine_grid_farm.rotor_diameters, wind_directions=turbine_grid_flow_field.wind_directions, grid_resolution=3, ) turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_findex, turbine_grid.sorted_coord_indices, ) turbine_grid_flow_field.initialize_velocity_field(turbine_grid) turbine_grid_farm.initialize(turbine_grid.sorted_indices) sequential_solver(turbine_grid_farm, turbine_grid_flow_field, turbine_grid, model_manager) ### Referring to the quantities from above, calculate the wake in the full grid # Use full flow_field here to use the full grid in the wake models deflection_model_args = model_manager.deflection_model.prepare_function( flow_field_grid, flow_field ) deficit_model_args = model_manager.velocity_model.prepare_function( flow_field_grid, flow_field ) wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(flow_field_grid.n_turbines): # Get the current turbine quantities x_i = np.mean(turbine_grid.x_sorted[:, i:i+1], axis=(2, 3)) x_i = x_i[:, :, None, None] y_i = np.mean(turbine_grid.y_sorted[:, i:i+1], axis=(2, 3)) y_i = y_i[:, :, None, None] z_i = np.mean(turbine_grid.z_sorted[:, i:i+1], axis=(2, 3)) z_i = z_i[:, :, None, None] u_i = turbine_grid_flow_field.u_sorted[:, i:i+1] v_i = turbine_grid_flow_field.v_sorted[:, i:i+1] ct_i = thrust_coefficient( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_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, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_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, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the thrust_coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_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, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_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, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] turbulence_intensity_i = \ turbine_grid_flow_field.turbulence_intensity_field_sorted_avg[:, i:i+1] yaw_angle_i = turbine_grid_farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = turbine_grid_farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = turbine_grid_farm.rotor_diameters_sorted[:, i:i+1, None, None] TSR_i = turbine_grid_farm.TSRs_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: added_yaw = wake_added_yaw( u_i, v_i, turbine_grid_flow_field.u_initial_sorted, turbine_grid.y_sorted[:, i:i+1] - y_i, turbine_grid.z_sorted[:, i:i+1], rotor_diameter_i, hub_height_i, ct_i, TSR_i, axial_induction_i, flow_field.wind_shear, ) effective_yaw_i += added_yaw # Model calculations # NOTE: exponential deflection_field = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, turbulence_intensity_i, ct_i, rotor_diameter_i, **deflection_model_args, ) if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( u_i, flow_field.u_initial_sorted, flow_field.dudz_initial_sorted, flow_field_grid.x_sorted - x_i, flow_field_grid.y_sorted - y_i, flow_field_grid.z_sorted, rotor_diameter_i, hub_height_i, yaw_angle_i, ct_i, TSR_i, axial_induction_i, flow_field.wind_shear, ) # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, axial_induction_i, deflection_field, yaw_angle_i, turbulence_intensity_i, ct_i, hub_height_i, rotor_diameter_i, **deficit_model_args, ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake
[docs] def cc_solver( farm: Farm, flow_field: FlowField, grid: TurbineGrid, model_manager: WakeModelManager ) -> None: # <<interface>> deflection_model_args = model_manager.deflection_model.prepare_function(grid, flow_field) deficit_model_args = model_manager.velocity_model.prepare_function(grid, flow_field) # This is u_wake v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) turb_u_wake = np.zeros_like(flow_field.u_initial_sorted) turb_inflow_field = copy.deepcopy(flow_field.u_initial_sorted) # Set up turbulence arrays turbine_turbulence_intensity = flow_field.turbulence_intensities[:, None, None, None] turbine_turbulence_intensity = np.repeat(turbine_turbulence_intensity, farm.n_turbines, axis=1) # Ambient turbulent intensity should be a copy of n_findex-long turbulence_intensities # with extra dimension to reach 4d ambient_turbulence_intensities = flow_field.turbulence_intensities.copy() ambient_turbulence_intensities = ambient_turbulence_intensities[:, None, None, None] shape = (farm.n_turbines,) + np.shape(flow_field.u_initial_sorted) Ctmp = np.zeros((shape)) # Ctmp = np.zeros((len(x_coord), len(wd), len(ws), len(x_coord), y_ngrid, z_ngrid)) # sigma_i = np.zeros((shape)) # sigma_i = np.zeros((len(x_coord), len(wd), len(ws), len(x_coord), y_ngrid, z_ngrid)) # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(grid.n_turbines): # Get the current turbine quantities x_i = np.mean(grid.x_sorted[:, i:i+1], axis=(2, 3)) x_i = x_i[:, :, None, None] y_i = np.mean(grid.y_sorted[:, i:i+1], axis=(2, 3)) y_i = y_i[:, :, None, None] z_i = np.mean(grid.z_sorted[:, i:i+1], axis=(2, 3)) z_i = z_i[:, :, None, None] rotor_diameter_i = farm.rotor_diameters_sorted[:, i:i+1, None, None] mask2 = ( (grid.x_sorted < x_i + 0.01) * (grid.x_sorted > x_i - 0.01) * (grid.y_sorted < y_i + 0.51 * rotor_diameter_i) * (grid.y_sorted > y_i - 0.51 * rotor_diameter_i) ) turb_inflow_field = ( turb_inflow_field * ~mask2 + (flow_field.u_initial_sorted - turb_u_wake) * mask2 ) turb_avg_vels = average_velocity(turb_inflow_field) turb_Cts = thrust_coefficient( turb_avg_vels, flow_field.turbulence_intensity_field_sorted, flow_field.air_density, farm.yaw_angles_sorted, farm.tilt_angles_sorted, farm.power_setpoints_sorted, farm.awc_modes_sorted, farm.awc_amplitudes_sorted, farm.turbine_thrust_coefficient_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) turb_Cts = turb_Cts[:, :, None, None] turb_aIs = axial_induction( turb_avg_vels, flow_field.turbulence_intensity_field_sorted, flow_field.air_density, farm.yaw_angles_sorted, farm.tilt_angles_sorted, farm.power_setpoints_sorted, farm.awc_modes_sorted, farm.awc_amplitudes_sorted, farm.turbine_axial_induction_functions, tilt_interps=farm.turbine_tilt_interps, correct_cp_ct_for_tilt=farm.correct_cp_ct_for_tilt_sorted, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) turb_aIs = turb_aIs[:, :, None, None] u_i = turb_inflow_field[:, i:i+1] v_i = flow_field.v_sorted[:, i:i+1] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) axial_induction_i = axial_induction_i[:, :, None, None] turbulence_intensity_i = turbine_turbulence_intensity[:, i:i+1] yaw_angle_i = farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = farm.hub_heights_sorted[:, i:i+1, None, None] TSR_i = farm.TSRs_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: added_yaw = wake_added_yaw( u_i, v_i, flow_field.u_initial_sorted, grid.y_sorted[:, i:i+1] - y_i, grid.z_sorted[:, i:i+1], rotor_diameter_i, hub_height_i, turb_Cts[:, i:i+1], TSR_i, axial_induction_i, flow_field.wind_shear, scale=2.0, ) effective_yaw_i += added_yaw # Model calculations # NOTE: exponential deflection_field = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, turbulence_intensity_i, turb_Cts[:, i:i+1], rotor_diameter_i, **deflection_model_args, ) if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( u_i, flow_field.u_initial_sorted, flow_field.dudz_initial_sorted, grid.x_sorted - x_i, grid.y_sorted - y_i, grid.z_sorted, rotor_diameter_i, hub_height_i, yaw_angle_i, turb_Cts[:, i:i+1], TSR_i, axial_induction_i, flow_field.wind_shear, scale=2.0, ) if model_manager.enable_yaw_added_recovery: I_mixing = yaw_added_turbulence_mixing( u_i, turbulence_intensity_i, v_i, flow_field.w_sorted[:, i:i+1], v_wake[:, i:i+1], w_wake[:, i:i+1], ) gch_gain = 1.0 turbine_turbulence_intensity[:, i:i+1] = turbulence_intensity_i + gch_gain * I_mixing turb_u_wake, Ctmp = model_manager.velocity_model.function( i, x_i, y_i, z_i, u_i, deflection_field, yaw_angle_i, turbine_turbulence_intensity, turb_Cts, farm.rotor_diameters_sorted[:, :, None, None], turb_u_wake, Ctmp, **deficit_model_args, ) wake_added_turbulence_intensity = model_manager.turbulence_model.function( ambient_turbulence_intensities, grid.x_sorted, x_i, rotor_diameter_i, turb_aIs ) # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = 1 - ( np.sum(turb_u_wake <= 0.05, axis=(2, 3)) / (grid.grid_resolution * grid.grid_resolution) ) area_overlap = area_overlap[:, :, None, None] # Modify wake added turbulence by wake area overlap downstream_influence_length = 15 * rotor_diameter_i ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) * (grid.x_sorted > x_i) * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbine_turbulence_intensity = np.maximum( np.sqrt(ti_added**2 + ambient_turbulence_intensities**2), turbine_turbulence_intensity ) flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.u_sorted = turb_inflow_field flow_field.turbulence_intensity_field_sorted = turbine_turbulence_intensity flow_field.turbulence_intensity_field_sorted_avg = np.mean( turbine_turbulence_intensity, axis=(2,3) )
[docs] def full_flow_cc_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid | FlowFieldPlanarGrid | PointsGrid, model_manager: WakeModelManager, ) -> None: # Get the flow quantities and turbine performance turbine_grid_farm = copy.deepcopy(farm) turbine_grid_flow_field = copy.deepcopy(flow_field) turbine_grid_farm.construct_turbine_map() turbine_grid_farm.construct_turbine_thrust_coefficient_functions() turbine_grid_farm.construct_turbine_axial_induction_functions() turbine_grid_farm.construct_turbine_power_functions() turbine_grid_farm.construct_hub_heights() turbine_grid_farm.construct_rotor_diameters() turbine_grid_farm.construct_turbine_TSRs() turbine_grid_farm.construct_turbine_ref_tilts() turbine_grid_farm.construct_turbine_tilt_interps() turbine_grid_farm.construct_turbine_correct_cp_ct_for_tilt() turbine_grid_farm.set_tilt_to_ref_tilt(flow_field.n_findex) turbine_grid = TurbineGrid( turbine_coordinates=turbine_grid_farm.coordinates, turbine_diameters=turbine_grid_farm.rotor_diameters, wind_directions=turbine_grid_flow_field.wind_directions, grid_resolution=3, ) turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_findex, turbine_grid.sorted_coord_indices, ) turbine_grid_flow_field.initialize_velocity_field(turbine_grid) turbine_grid_farm.initialize(turbine_grid.sorted_indices) cc_solver(turbine_grid_farm, turbine_grid_flow_field, turbine_grid, model_manager) ### Referring to the quantities from above, calculate the wake in the full grid # Use full flow_field here to use the full grid in the wake models deflection_model_args = model_manager.deflection_model.prepare_function( flow_field_grid, flow_field ) deficit_model_args = model_manager.velocity_model.prepare_function( flow_field_grid, flow_field ) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) turb_u_wake = np.zeros_like(flow_field.u_initial_sorted) shape = (farm.n_turbines,) + np.shape(flow_field.u_initial_sorted) Ctmp = np.zeros((shape)) # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(flow_field_grid.n_turbines): # Get the current turbine quantities x_i = np.mean(turbine_grid.x_sorted[:, i:i+1], axis=(2, 3)) x_i = x_i[:, :, None, None] y_i = np.mean(turbine_grid.y_sorted[:, i:i+1], axis=(2, 3)) y_i = y_i[:, :, None, None] z_i = np.mean(turbine_grid.z_sorted[:, i:i+1], axis=(2, 3)) z_i = z_i[:, :, None, None] u_i = turbine_grid_flow_field.u_sorted[:, i:i+1] v_i = turbine_grid_flow_field.v_sorted[:, i:i+1] turb_avg_vels = average_velocity(turbine_grid_flow_field.u_sorted) turb_Cts = thrust_coefficient( velocities=turb_avg_vels, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_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, awc_modes=turbine_grid_farm.awc_modes, awc_amplitudes=turbine_grid_farm.awc_amplitudes_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, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) turb_Cts = turb_Cts[:, :, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_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, awc_modes=turbine_grid_farm.awc_modes, awc_amplitudes=turbine_grid_farm.awc_amplitudes_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, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) axial_induction_i = axial_induction_i[:, :, None, None] turbulence_intensity_i = \ turbine_grid_flow_field.turbulence_intensity_field_sorted_avg[:, i:i+1] yaw_angle_i = turbine_grid_farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = turbine_grid_farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = turbine_grid_farm.rotor_diameters_sorted[:, i:i+1, None, None] TSR_i = turbine_grid_farm.TSRs_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: added_yaw = wake_added_yaw( u_i, v_i, turbine_grid_flow_field.u_initial_sorted, turbine_grid.y_sorted[:, i:i+1] - y_i, turbine_grid.z_sorted[:, i:i+1], rotor_diameter_i, hub_height_i, turb_Cts[:, i:i+1], TSR_i, axial_induction_i, flow_field.wind_shear, scale=2.0, ) effective_yaw_i += added_yaw # Model calculations # NOTE: exponential deflection_field = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, turbulence_intensity_i, turb_Cts[:, i:i+1], rotor_diameter_i, **deflection_model_args, ) if model_manager.enable_transverse_velocities: v_wake, w_wake = calculate_transverse_velocity( u_i, flow_field.u_initial_sorted, flow_field.dudz_initial_sorted, flow_field_grid.x_sorted - x_i, flow_field_grid.y_sorted - y_i, flow_field_grid.z_sorted, rotor_diameter_i, hub_height_i, yaw_angle_i, turb_Cts[:, i:i+1], TSR_i, axial_induction_i, flow_field.wind_shear, scale=2.0, ) # NOTE: exponential turb_u_wake, Ctmp = model_manager.velocity_model.function( i, x_i, y_i, z_i, u_i, deflection_field, yaw_angle_i, turbine_grid_flow_field.turbulence_intensity_field_sorted_avg, turb_Cts, turbine_grid_farm.rotor_diameters_sorted[:, :, None, None], turb_u_wake, Ctmp, **deficit_model_args, ) flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.u_sorted = flow_field.u_initial_sorted - turb_u_wake
[docs] def turbopark_solver( farm: Farm, flow_field: FlowField, grid: TurbineGrid, model_manager: WakeModelManager ) -> None: # Algorithm # For each turbine, calculate its effect on every downstream turbine. # For the current turbine, we are calculating the deficit that it adds to downstream turbines. # Integrate this into the main data structure. # Move on to the next turbine. # <<interface>> deflection_model_args = model_manager.deflection_model.prepare_function(grid, flow_field) deficit_model_args = model_manager.velocity_model.prepare_function(grid, flow_field) # This is u_wake wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) shape = (farm.n_turbines,) + np.shape(flow_field.u_initial_sorted) velocity_deficit = np.zeros(shape) deflection_field = np.zeros_like(flow_field.u_initial_sorted) # Set up turbulence arrays turbine_turbulence_intensity = flow_field.turbulence_intensities[:, None, None, None] turbine_turbulence_intensity = np.repeat(turbine_turbulence_intensity, farm.n_turbines, axis=1) # Ambient turbulent intensity should be a copy of n_findex-long turbulence_intensities # with extra dimension to reach 4d ambient_turbulence_intensities = flow_field.turbulence_intensities.copy() ambient_turbulence_intensities = ambient_turbulence_intensities[:, None, None, None] # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(grid.n_turbines): # Get the current turbine quantities x_i = np.mean(grid.x_sorted[:, i:i+1], axis=(2, 3)) x_i = x_i[:, :, None, None] y_i = np.mean(grid.y_sorted[:, i:i+1], axis=(2, 3)) y_i = y_i[:, :, None, None] z_i = np.mean(grid.z_sorted[:, i:i+1], axis=(2, 3)) z_i = z_i[:, :, None, None] Cts = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) ct_i = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the thrust coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] yaw_angle_i = farm.yaw_angles_sorted[:, i:i+1, None, None] rotor_diameter_i = farm.rotor_diameters_sorted[:, i:i+1, None, None] effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i if model_manager.enable_secondary_steering: raise NotImplementedError( "Secondary steering not available for this model.") # Model calculations # NOTE: exponential if np.any(farm.yaw_angles_sorted): model_manager.deflection_model.logger.warning( "WARNING: Deflection with the TurbOPark model has not been fully validated. " "This is an initial implementation, and we advise you use at your own risk " "and perform a thorough examination of the results." ) for ii in range(i): x_ii = np.mean(grid.x_sorted[:, ii:ii+1], axis=(2, 3)) x_ii = x_ii[:, :, None, None] y_ii = np.mean(grid.y_sorted[:, ii:ii+1], axis=(2, 3)) y_ii = y_ii[:, :, None, None] yaw_ii = farm.yaw_angles_sorted[:, ii:ii+1, None, None] turbulence_intensity_ii = turbine_turbulence_intensity[:, ii:ii+1] ct_ii = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[ii], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) ct_ii = ct_ii[:, 0:1, None, None] rotor_diameter_ii = farm.rotor_diameters_sorted[:, ii:ii+1, None, None] deflection_field_ii = model_manager.deflection_model.function( x_ii, y_ii, yaw_ii, turbulence_intensity_ii, ct_ii, rotor_diameter_ii, **deflection_model_args, ) deflection_field[:, ii:ii+1, :, :] = deflection_field_ii[:, i:i+1, :, :] if model_manager.enable_transverse_velocities: raise NotImplementedError( "Transverse velocities not used in this model.") if model_manager.enable_yaw_added_recovery: raise NotImplementedError( "Yaw added recovery not used in this model.") # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, turbine_turbulence_intensity, Cts[:, :, None, None], rotor_diameter_i, farm.rotor_diameters_sorted[:, :, None, None], i, deflection_field, **deficit_model_args, ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) wake_added_turbulence_intensity = model_manager.turbulence_model.function( ambient_turbulence_intensities, grid.x_sorted, x_i, rotor_diameter_i, axial_induction_i ) # TODO: leaving this in for GCH quantities; will need to find another way to # compute area_overlap as the current wake deficit is solved for only upstream # turbines; could use WAT_upstream # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = ( np.sum(velocity_deficit * flow_field.u_initial_sorted > 0.05, axis=(2, 3)) / (grid.grid_resolution * grid.grid_resolution) ) area_overlap = area_overlap[:, :, None, None] # Modify wake added turbulence by wake area overlap downstream_influence_length = 15 * rotor_diameter_i ti_added = ( area_overlap * np.nan_to_num(wake_added_turbulence_intensity, posinf=0.0) * (grid.x_sorted > x_i) * (np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) * (grid.x_sorted <= downstream_influence_length + x_i) ) # Combine turbine TIs with WAT turbine_turbulence_intensity = np.maximum( np.sqrt(ti_added**2 + ambient_turbulence_intensities**2), turbine_turbulence_intensity ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake flow_field.turbulence_intensity_field_sorted = turbine_turbulence_intensity flow_field.turbulence_intensity_field_sorted_avg = np.mean( turbine_turbulence_intensity, axis=(2, 3) )
[docs] def full_flow_turbopark_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid, model_manager: WakeModelManager ) -> None: raise NotImplementedError("Plotting for the TurbOPark model is not currently implemented.")
[docs] def empirical_gauss_solver( farm: Farm, flow_field: FlowField, grid: TurbineGrid, model_manager: WakeModelManager ) -> NDArrayFloat: """ Algorithm: For each turbine, calculate its effect on every downstream turbine. For the current turbine, we are calculating the deficit that it adds to downstream turbines. Integrate this into the main data structure. Move on to the next turbine. Args: farm (Farm) flow_field (FlowField) grid (TurbineGrid) model_manager (WakeModelManager) Raises: NotImplementedError: Raised if secondary steering is enabled with the EmGauss model. NotImplementedError: Raised if transverse velocities is enabled with the EmGauss model. Returns: NDArrayFloat: wake induced mixing field primarily for use in the full-flow EmGauss solver """ # <<interface>> deflection_model_args = model_manager.deflection_model.prepare_function(grid, flow_field) deficit_model_args = model_manager.velocity_model.prepare_function(grid, flow_field) # This is u_wake wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) x_locs = np.mean(grid.x_sorted, axis=(2, 3))[:,:,None] downstream_distance_D = x_locs - np.transpose(x_locs, axes=(0,2,1)) downstream_distance_D = downstream_distance_D / \ np.repeat(farm.rotor_diameters_sorted[:,:,None], grid.n_turbines, axis=-1) downstream_distance_D = np.maximum(downstream_distance_D, 0.1) # For ease # Initialize the mixing factor model using TI if specified initial_mixing_factor = model_manager.turbulence_model.atmospheric_ti_gain * np.eye( grid.n_turbines ) mixing_factor = np.repeat( initial_mixing_factor[None, :, :], flow_field.n_findex, axis=0 ) mixing_factor = mixing_factor * flow_field.turbulence_intensities[:, None, None] # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(grid.n_turbines): # Get the current turbine quantities x_i = np.mean(grid.x_sorted[:, i:i+1], axis=(2, 3)) x_i = x_i[:, :, None, None] y_i = np.mean(grid.y_sorted[:, i:i+1], axis=(2, 3)) y_i = y_i[:, :, None, None] z_i = np.mean(grid.z_sorted[:, i:i+1], axis=(2, 3)) z_i = z_i[:, :, None, None] ct_i = thrust_coefficient( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the thrust coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=flow_field.u_sorted, turbulence_intensities=flow_field.turbulence_intensity_field_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, awc_modes=farm.awc_modes_sorted, awc_amplitudes=farm.awc_amplitudes_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, turbine_type_map=farm.turbine_type_map_sorted, turbine_power_thrust_tables=farm.turbine_power_thrust_tables, ix_filter=[i], average_method=grid.average_method, cubature_weights=grid.cubature_weights, multidim_condition=flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] yaw_angle_i = farm.yaw_angles_sorted[:, i:i+1, None, None] awc_mode_i = farm.awc_modes_sorted[:, i:i+1, None, None] awc_amplitude_i = farm.awc_amplitudes_sorted[:, i:i+1, None, None] awc_frequency_i = farm.awc_frequencies_sorted[:, i:i+1, None, None] hub_height_i = farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = farm.rotor_diameters_sorted[:, i:i+1, None, None] # Secondary steering not currently implemented in EmGauss model # effective_yaw_i = np.zeros_like(yaw_angle_i) # effective_yaw_i += yaw_angle_i average_velocities = average_velocity( flow_field.u_sorted, method=grid.average_method, cubature_weights=grid.cubature_weights ) tilt_angle_i = farm.calculate_tilt_for_eff_velocities(average_velocities) tilt_angle_i = tilt_angle_i[:, i:i+1, None, None] if model_manager.enable_secondary_steering: raise NotImplementedError( "Secondary steering not available for this model.") if model_manager.enable_transverse_velocities: raise NotImplementedError( "Transverse velocities not used in this model.") if model_manager.enable_yaw_added_recovery: # Influence of yawing on turbine's own wake mixing_factor[:, i:i+1, i] += \ yaw_added_wake_mixing( axial_induction_i, yaw_angle_i, 1, model_manager.deflection_model.yaw_added_mixing_gain ) if model_manager.enable_active_wake_mixing: # Influence of awc on turbine's own wake mixing_factor[:, i:i+1, i] += \ awc_added_wake_mixing( awc_mode_i, awc_amplitude_i, awc_frequency_i, model_manager.velocity_model.awc_wake_exp, model_manager.velocity_model.awc_wake_denominator ) # Extract total wake induced mixing for turbine i mixing_i = np.linalg.norm( mixing_factor[:, i:i+1, :, None], ord=2, axis=2, keepdims=True ) # Model calculations # NOTE: exponential deflection_field_y, deflection_field_z = model_manager.deflection_model.function( x_i, y_i, yaw_angle_i, tilt_angle_i, mixing_i, ct_i, rotor_diameter_i, **deflection_model_args ) # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, axial_induction_i, deflection_field_y, deflection_field_z, yaw_angle_i, tilt_angle_i, mixing_i, ct_i, hub_height_i, rotor_diameter_i, **deficit_model_args ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) # Calculate wake overlap for wake-added turbulence (WAT) area_overlap = np.sum(velocity_deficit * flow_field.u_initial_sorted > 0.05, axis=(2, 3))\ / (grid.grid_resolution * grid.grid_resolution) # Compute wake induced mixing factor mixing_factor[:,:,i] += \ area_overlap * model_manager.turbulence_model.function( axial_induction_i, downstream_distance_D[:,:,i] ) if model_manager.enable_yaw_added_recovery: mixing_factor[:,:,i] += \ area_overlap * yaw_added_wake_mixing( axial_induction_i, yaw_angle_i, downstream_distance_D[:,:,i], model_manager.deflection_model.yaw_added_mixing_gain ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake return mixing_factor
[docs] def full_flow_empirical_gauss_solver( farm: Farm, flow_field: FlowField, flow_field_grid: FlowFieldGrid, model_manager: WakeModelManager ) -> None: # Get the flow quantities and turbine performance turbine_grid_farm = copy.deepcopy(farm) turbine_grid_flow_field = copy.deepcopy(flow_field) turbine_grid_farm.construct_turbine_map() turbine_grid_farm.construct_turbine_thrust_coefficient_functions() turbine_grid_farm.construct_turbine_axial_induction_functions() turbine_grid_farm.construct_turbine_power_functions() turbine_grid_farm.construct_hub_heights() turbine_grid_farm.construct_rotor_diameters() turbine_grid_farm.construct_turbine_TSRs() turbine_grid_farm.construct_turbine_ref_tilts() turbine_grid_farm.construct_turbine_tilt_interps() turbine_grid_farm.construct_turbine_correct_cp_ct_for_tilt() turbine_grid_farm.set_tilt_to_ref_tilt(flow_field.n_findex) turbine_grid = TurbineGrid( turbine_coordinates=turbine_grid_farm.coordinates, turbine_diameters=turbine_grid_farm.rotor_diameters, wind_directions=turbine_grid_flow_field.wind_directions, grid_resolution=3, ) turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_findex, turbine_grid.sorted_coord_indices ) turbine_grid_flow_field.initialize_velocity_field(turbine_grid) turbine_grid_farm.initialize(turbine_grid.sorted_indices) wim_field = empirical_gauss_solver( turbine_grid_farm, turbine_grid_flow_field, turbine_grid, model_manager ) ### Referring to the quantities from above, calculate the wake in the full grid # Use full flow_field here to use the full grid in the wake models deflection_model_args = model_manager.deflection_model.prepare_function( flow_field_grid, flow_field ) deficit_model_args = model_manager.velocity_model.prepare_function(flow_field_grid, flow_field) wake_field = np.zeros_like(flow_field.u_initial_sorted) v_wake = np.zeros_like(flow_field.v_initial_sorted) w_wake = np.zeros_like(flow_field.w_initial_sorted) # Calculate the velocity deficit sequentially from upstream to downstream turbines for i in range(flow_field_grid.n_turbines): # Get the current turbine quantities x_i = np.mean(turbine_grid.x_sorted[:, i:i+1], axis=(2,3)) x_i = x_i[:, :, None, None] y_i = np.mean(turbine_grid.y_sorted[:, i:i+1], axis=(2,3)) y_i = y_i[:, :, None, None] z_i = np.mean(turbine_grid.z_sorted[:, i:i+1], axis=(2,3)) z_i = z_i[:, :, None, None] ct_i = thrust_coefficient( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_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, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_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, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the thrust coefficient function, # get the first index here (0:1) ct_i = ct_i[:, 0:1, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, turbulence_intensities=turbine_grid_flow_field.turbulence_intensity_field_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, awc_modes=turbine_grid_farm.awc_modes_sorted, awc_amplitudes=turbine_grid_farm.awc_amplitudes_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, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, turbine_power_thrust_tables=turbine_grid_farm.turbine_power_thrust_tables, ix_filter=[i], average_method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights, multidim_condition=turbine_grid_flow_field.multidim_conditions, ) # Since we are filtering for the i'th turbine in the axial induction function, # get the first index here (0:1) axial_induction_i = axial_induction_i[:, 0:1, None, None] yaw_angle_i = turbine_grid_farm.yaw_angles_sorted[:, i:i+1, None, None] hub_height_i = turbine_grid_farm.hub_heights_sorted[:, i:i+1, None, None] rotor_diameter_i = turbine_grid_farm.rotor_diameters_sorted[:, i:i+1, None, None] wake_induced_mixing_i = wim_field[:, i:i+1, :, None].sum(axis=2, keepdims=1) effective_yaw_i = np.zeros_like(yaw_angle_i) effective_yaw_i += yaw_angle_i average_velocities = average_velocity( turbine_grid_flow_field.u_sorted, method=turbine_grid.average_method, cubature_weights=turbine_grid.cubature_weights ) tilt_angle_i = turbine_grid_farm.calculate_tilt_for_eff_velocities(average_velocities) tilt_angle_i = tilt_angle_i[:, i:i+1, None, None] if model_manager.enable_secondary_steering: raise NotImplementedError( "Secondary steering not available for this model.") if model_manager.enable_transverse_velocities: raise NotImplementedError( "Transverse velocities not used in this model.") # Model calculations # NOTE: exponential deflection_field_y, deflection_field_z = model_manager.deflection_model.function( x_i, y_i, effective_yaw_i, tilt_angle_i, wake_induced_mixing_i, ct_i, rotor_diameter_i, **deflection_model_args ) # NOTE: exponential velocity_deficit = model_manager.velocity_model.function( x_i, y_i, z_i, axial_induction_i, deflection_field_y, deflection_field_z, yaw_angle_i, tilt_angle_i, wake_induced_mixing_i, ct_i, hub_height_i, rotor_diameter_i, **deficit_model_args ) wake_field = model_manager.combination_model.function( wake_field, velocity_deficit * flow_field.u_initial_sorted ) flow_field.u_sorted = flow_field.u_initial_sorted - wake_field flow_field.v_sorted += v_wake flow_field.w_sorted += w_wake