def test_axial_induction(): N_TURBINES = 4 turbine_data = SampleInputs().turbine turbine = Turbine.from_dict(turbine_data) turbine_type_map = np.array(N_TURBINES * [turbine.turbine_type]) turbine_type_map = turbine_type_map[None, None, :] baseline_ai = 0.25116283939089806 # Single turbine wind_speed = 10.0 ai = axial_induction( velocities=wind_speed * np.ones((1, 1, 1, 3, 3)), yaw_angle=np.zeros((1, 1, 1)), fCt=np.array([(turbine.turbine_type, turbine.fCt_interp)]), turbine_type_map=turbine_type_map[0,0,0], ) np.testing.assert_allclose(ai, baseline_ai) # Multiple turbines with ix filter ai = axial_induction( velocities=np.ones((N_TURBINES, 3, 3)) * WIND_CONDITION_BROADCAST, # 3 x 4 x 4 x 3 x 3 yaw_angle=np.zeros((1, 1, N_TURBINES)), fCt=np.array([(turbine.turbine_type, turbine.fCt_interp)]), turbine_type_map=turbine_type_map, ix_filter=INDEX_FILTER, ) assert len(ai[0, 0]) == len(INDEX_FILTER) # Test the 10 m/s wind speed to use the same baseline as above np.testing.assert_allclose(ai[0,2], baseline_ai)
def test_regression_tandem(sample_inputs_fixture): """ Tandem turbines """ sample_inputs_fixture.floris["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.floris["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.floris["wake"]["model_strings"]["combination_model"] = COMBINATION_MODEL floris = Floris.from_dict(sample_inputs_fixture.floris) floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_wind_speeds = floris.flow_field.n_wind_speeds n_wind_directions = floris.flow_field.n_wind_directions velocities = floris.flow_field.u yaw_angles = floris.farm.yaw_angles test_results = np.zeros((n_wind_directions, n_wind_speeds, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = Ct( velocities, yaw_angles, floris.farm.turbine_fCts, floris.farm.turbine_type_map, ) farm_powers = power( floris.flow_field.air_density, velocities, yaw_angles, floris.farm.pPs, floris.farm.turbine_power_interps, floris.farm.turbine_type_map, ) farm_axial_inductions = axial_induction( velocities, yaw_angles, floris.farm.turbine_fCts, floris.farm.turbine_type_map, ) for i in range(n_wind_directions): for j in range(n_wind_speeds): for k in range(n_turbines): test_results[i, j, k, 0] = farm_avg_velocities[i, j, k] test_results[i, j, k, 1] = farm_cts[i, j, k] test_results[i, j, k, 2] = farm_powers[i, j, k] test_results[i, j, k, 3] = farm_axial_inductions[i, j, k] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, ) assert_results_arrays(test_results[0], baseline)
def full_flow_cc_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_fCts() turbine_grid_farm.construct_turbine_fCps() turbine_grid_farm.construct_turbine_power_interps() turbine_grid_farm.construct_hub_heights() turbine_grid_farm.construct_rotor_diameters() turbine_grid_farm.construct_turbine_TSRs() turbine_grid_farm.construc_turbine_pPs() turbine_grid_farm.construct_coordinates() turbine_grid = TurbineGrid( turbine_coordinates=turbine_grid_farm.coordinates, reference_turbine_diameter=turbine_grid_farm.rotor_diameters_sorted, wind_directions=turbine_grid_flow_field.wind_directions, wind_speeds=turbine_grid_flow_field.wind_speeds, grid_resolution=3, ) turbine_grid_farm.expand_farm_properties( turbine_grid_flow_field.n_wind_directions, turbine_grid_flow_field.n_wind_speeds, 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=(3, 4)) x_i = x_i[:, :, :, None, None] y_i = np.mean(turbine_grid.y_sorted[:, :, i:i+1], axis=(3, 4)) y_i = y_i[:, :, :, None, None] z_i = np.mean(turbine_grid.z_sorted[:, :, i:i+1], axis=(3, 4)) 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 = Ct( velocities=turb_avg_vels, yaw_angle=turbine_grid_farm.yaw_angles_sorted, fCt=turbine_grid_farm.turbine_fCts, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, ) turb_Cts = turb_Cts[:, :, :, None, None] axial_induction_i = axial_induction( velocities=turbine_grid_flow_field.u_sorted, yaw_angle=turbine_grid_farm.yaw_angles_sorted, fCt=turbine_grid_farm.turbine_fCts, turbine_type_map=turbine_grid_farm.turbine_type_map_sorted, ix_filter=[i], ) axial_induction_i = axial_induction_i[:, :, :, None, None] turbulence_intensity_i = turbine_grid_flow_field.turbulence_intensity_field[:, :, 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, 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_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, 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, 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
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) turbine_turbulence_intensity = flow_field.turbulence_intensity * np.ones((flow_field.n_wind_directions, flow_field.n_wind_speeds, farm.n_turbines, 1, 1)) ambient_turbulence_intensity = flow_field.turbulence_intensity # 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=(3, 4)) x_i = x_i[:, :, :, None, None] y_i = np.mean(grid.y_sorted[:, :, i:i+1], axis=(3, 4)) y_i = y_i[:, :, :, None, None] z_i = np.mean(grid.z_sorted[:, :, i:i+1], axis=(3, 4)) 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 = Ct( velocities=flow_field.u_sorted, yaw_angle=farm.yaw_angles_sorted, fCt=farm.turbine_fCts, turbine_type_map=farm.turbine_type_map_sorted, ix_filter=[i], ) ct_i = ct_i[:, :, 0:1, None, None] # Since we are filtering for the i'th turbine in the Ct function, get the first index here (0:1) axial_induction_i = axial_induction( velocities=flow_field.u_sorted, yaw_angle=farm.yaw_angles_sorted, fCt=farm.turbine_fCts, turbine_type_map=farm.turbine_type_map_sorted, ix_filter=[i], ) axial_induction_i = axial_induction_i[:, :, 0:1, None, None] # Since we are filtering for the i'th turbine in the axial induction function, get the first index here (0:1) 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 ) 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, 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 ) 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_intensity, 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=(3, 4)) / (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) * np.array(grid.x_sorted > x_i) * np.array(np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) * np.array(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_intensity ** 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 = np.mean(turbine_turbulence_intensity, axis=(3,4)) flow_field.turbulence_intensity_field = flow_field.turbulence_intensity_field[:,:,:,None,None]
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) turbine_turbulence_intensity = flow_field.turbulence_intensity * np.ones((flow_field.n_wind_directions, flow_field.n_wind_speeds, farm.n_turbines, 1, 1)) ambient_turbulence_intensity = flow_field.turbulence_intensity 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=(3, 4)) x_i = x_i[:, :, :, None, None] y_i = np.mean(grid.y_sorted[:, :, i:i+1], axis=(3, 4)) y_i = y_i[:, :, :, None, None] z_i = np.mean(grid.z_sorted[:, :, i:i+1], axis=(3, 4)) z_i = z_i[:, :, :, None, None] mask2 = np.array(grid.x_sorted < x_i + 0.01) * np.array(grid.x_sorted > x_i - 0.01) * np.array(grid.y_sorted < y_i + 0.51*126.0) * np.array(grid.y_sorted > y_i - 0.51*126.0) # mask2 = np.logical_and(np.logical_and(np.logical_and(grid.x_sorted < x_i + 0.01, grid.x_sorted > x_i - 0.01), grid.y_sorted < y_i + 0.51*126.0), grid.y_sorted > y_i - 0.51*126.0) 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 = Ct( turb_avg_vels, farm.yaw_angles_sorted, farm.turbine_fCts, turbine_type_map=farm.turbine_type_map_sorted, ) turb_Cts = turb_Cts[:, :, :, None, None] turb_aIs = axial_induction( turb_avg_vels, farm.yaw_angles_sorted, farm.turbine_fCts, turbine_type_map=farm.turbine_type_map_sorted, ix_filter=[i], ) 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, yaw_angle=farm.yaw_angles_sorted, fCt=farm.turbine_fCts, turbine_type_map=farm.turbine_type_map_sorted, ix_filter=[i], ) 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] 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, turb_Cts[:, :, i:i+1], TSR_i, axial_induction_i, 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, 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, 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_intensity, 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=(3, 4)) / (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) * np.array(grid.x_sorted > x_i) * np.array(np.abs(y_i - grid.y_sorted) < 2 * rotor_diameter_i) * np.array(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_intensity ** 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 = np.mean(turbine_turbulence_intensity, axis=(3,4)) flow_field.turbulence_intensity_field = flow_field.turbulence_intensity_field[:,:,:,None,None]
def test_regression_secondary_steering(sample_inputs_fixture): """ Tandem turbines with the upstream turbine yawed and secondary steering enabled """ sample_inputs_fixture.floris["wake"]["model_strings"]["velocity_model"] = VELOCITY_MODEL sample_inputs_fixture.floris["wake"]["model_strings"]["deflection_model"] = DEFLECTION_MODEL sample_inputs_fixture.floris["wake"]["enable_transverse_velocities"] = True sample_inputs_fixture.floris["wake"]["enable_secondary_steering"] = True sample_inputs_fixture.floris["wake"]["enable_yaw_added_recovery"] = False floris = Floris.from_dict(sample_inputs_fixture.floris) yaw_angles = np.zeros((N_WIND_DIRECTIONS, N_WIND_SPEEDS, N_TURBINES)) yaw_angles[:,:,0] = 5.0 floris.farm.yaw_angles = yaw_angles floris.initialize_domain() floris.steady_state_atmospheric_condition() n_turbines = floris.farm.n_turbines n_wind_speeds = floris.flow_field.n_wind_speeds n_wind_directions = floris.flow_field.n_wind_directions velocities = floris.flow_field.u yaw_angles = floris.farm.yaw_angles test_results = np.zeros((n_wind_directions, n_wind_speeds, n_turbines, 4)) farm_avg_velocities = average_velocity( velocities, ) farm_cts = Ct( velocities, yaw_angles, floris.farm.turbine_fCts, floris.farm.turbine_type_map, ) farm_powers = power( floris.flow_field.air_density, velocities, yaw_angles, floris.farm.pPs, floris.farm.turbine_power_interps, floris.farm.turbine_type_map, ) farm_axial_inductions = axial_induction( velocities, yaw_angles, floris.farm.turbine_fCts, floris.farm.turbine_type_map, ) for i in range(n_wind_directions): for j in range(n_wind_speeds): for k in range(n_turbines): test_results[i, j, k, 0] = farm_avg_velocities[i, j, k] test_results[i, j, k, 1] = farm_cts[i, j, k] test_results[i, j, k, 2] = farm_powers[i, j, k] test_results[i, j, k, 3] = farm_axial_inductions[i, j, k] if DEBUG: print_test_values( farm_avg_velocities, farm_cts, farm_powers, farm_axial_inductions, ) assert_results_arrays(test_results[0], secondary_steering_baseline)