diff --git a/examples/01_opening_floris_computing_power.py b/examples/01_opening_floris_computing_power.py index 4e7818df6..52935a956 100644 --- a/examples/01_opening_floris_computing_power.py +++ b/examples/01_opening_floris_computing_power.py @@ -19,17 +19,16 @@ fi = FlorisInterface("inputs/gch.yaml") # Convert to a simple two turbine layout -fi.reinitialize(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) +fi.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) # Single wind speed and wind direction print("\n========================= Single Wind Direction and Wind Speed =========================") # Get the turbine powers assuming 1 wind direction and speed -fi.reinitialize(wind_directions=[270.0], wind_speeds=[8.0]) +# Set the yaw angles to 0 with 1 wind direction and speed +fi.set(wind_directions=[270.0], wind_speeds=[8.0], yaw_angles=np.zeros([1, 2])) -# Set the yaw angles to 0 -yaw_angles = np.zeros([1, 2]) # 1 wind direction and speed, 2 turbines -fi.calculate_wake(yaw_angles=yaw_angles) +fi.run() # Get the turbine powers turbine_powers = fi.get_turbine_powers() / 1000.0 @@ -44,9 +43,9 @@ wind_speeds = np.array([8.0, 9.0, 10.0]) wind_directions = np.array([270.0, 270.0, 270.0]) -fi.reinitialize(wind_speeds=wind_speeds, wind_directions=wind_directions) -yaw_angles = np.zeros([3, 2]) # 3 wind directions/ speeds, 2 turbines -fi.calculate_wake(yaw_angles=yaw_angles) +# 3 wind directions/ speeds +fi.set(wind_speeds=wind_speeds, wind_directions=wind_directions, yaw_angles=np.zeros([3, 2])) +fi.run() turbine_powers = fi.get_turbine_powers() / 1000.0 print("The turbine power matrix should be of dimensions 3 findex X 2 Turbines") print(turbine_powers) @@ -60,9 +59,8 @@ wind_speeds = np.tile([8.0, 9.0, 10.0], 3) wind_directions = np.repeat([260.0, 270.0, 280.0], 3) -fi.reinitialize(wind_directions=wind_directions, wind_speeds=wind_speeds) -yaw_angles = np.zeros([9, 2]) # 9 wind directions/ speeds, 2 turbines -fi.calculate_wake(yaw_angles=yaw_angles) +fi.set(wind_directions=wind_directions, wind_speeds=wind_speeds, yaw_angles=np.zeros([9, 2])) +fi.run() turbine_powers = fi.get_turbine_powers() / 1000.0 print("The turbine power matrix should be of dimensions 9 WD/WS X 2 Turbines") print(turbine_powers) diff --git a/examples/02_visualizations.py b/examples/02_visualizations.py index a82f84ee8..496f2d41b 100644 --- a/examples/02_visualizations.py +++ b/examples/02_visualizations.py @@ -101,7 +101,7 @@ # Run the wake calculation to get the turbine-turbine interfactions # on the turbine grids -fi.calculate_wake() +fi.run() # Plot the values at each rotor fig, axes, _ , _ = wakeviz.plot_rotor_values( @@ -125,11 +125,11 @@ "type": "turbine_grid", "turbine_grid_points": 10 } -fi.reinitialize(solver_settings=solver_settings) +fi.set(solver_settings=solver_settings) # Run the wake calculation to get the turbine-turbine interfactions # on the turbine grids -fi.calculate_wake() +fi.run() # Plot the values at each rotor fig, axes, _ , _ = wakeviz.plot_rotor_values( diff --git a/examples/03_making_adjustments.py b/examples/03_making_adjustments.py index e405aea65..5c71bba2d 100644 --- a/examples/03_making_adjustments.py +++ b/examples/03_making_adjustments.py @@ -45,7 +45,7 @@ # Change the wind shear, reset the wind speed, and plot a vertical slice -fi.reinitialize( wind_shear=0.2, wind_speeds=[8.0] ) +fi.set(wind_shear=0.2, wind_speeds=[8.0]) y_plane = fi.calculate_y_plane(crossstream_dist=0.0) wakeviz.visualize_cut_plane( y_plane, @@ -61,7 +61,7 @@ 5.0 * fi.floris.farm.rotor_diameters[0,0] * np.arange(0, N, 1), 5.0 * fi.floris.farm.rotor_diameters[0,0] * np.arange(0, N, 1), ) -fi.reinitialize(layout_x=X.flatten(), layout_y=Y.flatten(), wind_directions=[270.0]) +fi.set(layout_x=X.flatten(), layout_y=Y.flatten(), wind_directions=[270.0]) horizontal_plane = fi.calculate_horizontal_plane(height=90.0) wakeviz.visualize_cut_plane( horizontal_plane, diff --git a/examples/04_sweep_wind_directions.py b/examples/04_sweep_wind_directions.py index a76ff6bb3..6cfa73612 100644 --- a/examples/04_sweep_wind_directions.py +++ b/examples/04_sweep_wind_directions.py @@ -22,12 +22,12 @@ D = 126. layout_x = np.array([0, D*6]) layout_y = [0, 0] -fi.reinitialize(layout_x=layout_x, layout_y=layout_y) +fi.set(layout_x=layout_x, layout_y=layout_y) # Sweep wind speeds but keep wind direction fixed wd_array = np.arange(250,291,1.) ws_array = 8.0 * np.ones_like(wd_array) -fi.reinitialize(wind_directions=wd_array, wind_speeds=ws_array) +fi.set(wind_directions=wd_array, wind_speeds=ws_array) # Define a matrix of yaw angles to be all 0 # Note that yaw angles is now specified as a matrix whose dimensions are @@ -37,9 +37,10 @@ n_findex = num_wd # Could be either num_wd or num_ws num_turbine = len(layout_x) # Number of turbines yaw_angles = np.zeros((n_findex, num_turbine)) +fi.set(yaw_angles=yaw_angles) # Calculate -fi.calculate_wake(yaw_angles=yaw_angles) +fi.run() # Collect the turbine powers turbine_powers = fi.get_turbine_powers() / 1E3 # In kW diff --git a/examples/05_sweep_wind_speeds.py b/examples/05_sweep_wind_speeds.py index b5b93e488..b9ce3c317 100644 --- a/examples/05_sweep_wind_speeds.py +++ b/examples/05_sweep_wind_speeds.py @@ -22,12 +22,12 @@ D = 126. layout_x = np.array([0, D*6]) layout_y = [0, 0] -fi.reinitialize(layout_x=layout_x, layout_y=layout_y) +fi.set(layout_x=layout_x, layout_y=layout_y) # Sweep wind speeds but keep wind direction fixed ws_array = np.arange(5,25,0.5) wd_array = 270.0 * np.ones_like(ws_array) -fi.reinitialize(wind_directions=wd_array,wind_speeds=ws_array) +fi.set(wind_directions=wd_array,wind_speeds=ws_array) # Define a matrix of yaw angles to be all 0 # Note that yaw angles is now specified as a matrix whose dimensions are @@ -37,9 +37,10 @@ n_findex = num_wd # Could be either num_wd or num_ws num_turbine = len(layout_x) yaw_angles = np.zeros((n_findex, num_turbine)) +fi.set(yaw_angles=yaw_angles) # Calculate -fi.calculate_wake(yaw_angles=yaw_angles) +fi.run() # Collect the turbine powers turbine_powers = fi.get_turbine_powers() / 1E3 # In kW diff --git a/examples/06_sweep_wind_conditions.py b/examples/06_sweep_wind_conditions.py index 9b6e28902..9debf07ca 100644 --- a/examples/06_sweep_wind_conditions.py +++ b/examples/06_sweep_wind_conditions.py @@ -27,7 +27,7 @@ D = 126.0 layout_x = np.array([0, D*6, D*12, D*18, D*24]) layout_y = [0, 0, 0, 0, 0] -fi.reinitialize(layout_x=layout_x, layout_y=layout_y) +fi.set(layout_x=layout_x, layout_y=layout_y) # In this case we want to check a grid of wind speed and direction combinations wind_speeds_to_expand = np.arange(6, 9, 1.0) @@ -46,7 +46,7 @@ wd_array = wind_directions_grid.flatten() # Now reinitialize FLORIS -fi.reinitialize(wind_speeds=ws_array, wind_directions=wd_array) +fi.set(wind_speeds=ws_array, wind_directions=wd_array) # Define a matrix of yaw angles to be all 0 # Note that yaw angles is now specified as a matrix whose dimensions are @@ -56,9 +56,10 @@ n_findex = num_wd # Could be either num_wd or num_ws num_turbine = len(layout_x) yaw_angles = np.zeros((n_findex, num_turbine)) +fi.set(yaw_angles=yaw_angles) # Calculate -fi.calculate_wake(yaw_angles=yaw_angles) +fi.run() # Collect the turbine powers turbine_powers = fi.get_turbine_powers() / 1e3 # In kW diff --git a/examples/07_calc_aep_from_rose.py b/examples/07_calc_aep_from_rose.py index ea1d8c9b9..18db25a71 100644 --- a/examples/07_calc_aep_from_rose.py +++ b/examples/07_calc_aep_from_rose.py @@ -47,7 +47,7 @@ # Assume a three-turbine wind farm with 5D spacing. We reinitialize the # floris object and assign the layout, wind speed and wind direction arrays. D = fi.floris.farm.rotor_diameters[0] # Rotor diameter for the NREL 5 MW -fi.reinitialize( +fi.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wind_directions, diff --git a/examples/09_compare_farm_power_with_neighbor.py b/examples/09_compare_farm_power_with_neighbor.py index b20359c83..c326eee71 100644 --- a/examples/09_compare_farm_power_with_neighbor.py +++ b/examples/09_compare_farm_power_with_neighbor.py @@ -24,16 +24,16 @@ D = 126. layout_x = np.array([0, D*6, 0, D*6]) layout_y = [0, 0, D*3, D*3] -fi.reinitialize(layout_x = layout_x, layout_y = layout_y) +fi.set(layout_x=layout_x, layout_y=layout_y) # Define a simple wind rose with just 1 wind speed wd_array = np.arange(0,360,4.) ws_array = 8.0 * np.ones_like(wd_array) -fi.reinitialize(wind_directions=wd_array, wind_speeds=ws_array) +fi.set(wind_directions=wd_array, wind_speeds=ws_array) # Calculate -fi.calculate_wake() +fi.run() # Collect the farm power farm_power_base = fi.get_farm_power() / 1E3 # In kW @@ -41,14 +41,14 @@ # Add a neighbor to the east layout_x = np.array([0, D*6, 0, D*6, D*12, D*15, D*12, D*15]) layout_y = np.array([0, 0, D*3, D*3, 0, 0, D*3, D*3]) -fi.reinitialize(layout_x = layout_x, layout_y = layout_y) +fi.set(layout_x=layout_x, layout_y=layout_y) # Define the weights to exclude the neighboring farm from calcuations of power turbine_weights = np.zeros(len(layout_x), dtype=int) turbine_weights[0:4] = 1.0 # Calculate -fi.calculate_wake() +fi.run() # Collect the farm power with the neightbor farm_power_neighbor = fi.get_farm_power(turbine_weights=turbine_weights) / 1E3 # In kW diff --git a/examples/10_opt_yaw_single_ws.py b/examples/10_opt_yaw_single_ws.py index 15d1c31bc..ac39b5b4e 100644 --- a/examples/10_opt_yaw_single_ws.py +++ b/examples/10_opt_yaw_single_ws.py @@ -23,7 +23,7 @@ wd_array = np.arange(0.0, 360.0, 3.0) ws_array = 8.0 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW -fi.reinitialize( +fi.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, diff --git a/examples/11_opt_yaw_multiple_ws.py b/examples/11_opt_yaw_multiple_ws.py index a3d38d307..798750e0b 100644 --- a/examples/11_opt_yaw_multiple_ws.py +++ b/examples/11_opt_yaw_multiple_ws.py @@ -35,7 +35,7 @@ # Reinitialize as a 3-turbine farm with range of WDs and WSs D = 126.0 # Rotor diameter for the NREL 5 MW -fi.reinitialize( +fi.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, diff --git a/examples/12_optimize_yaw.py b/examples/12_optimize_yaw.py index a1d676f23..55f1547c8 100644 --- a/examples/12_optimize_yaw.py +++ b/examples/12_optimize_yaw.py @@ -35,7 +35,7 @@ def load_floris(): 5.0 * fi.floris.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1), 5.0 * fi.floris.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1), ) - fi.reinitialize(layout_x=X.flatten(), layout_y=Y.flatten()) + fi.set(layout_x=X.flatten(), layout_y=Y.flatten()) return fi @@ -63,10 +63,10 @@ def calculate_aep(fi, df_windrose, column_name="farm_power"): wd_array = np.array(df_windrose["wd"], dtype=float) ws_array = np.array(df_windrose["ws"], dtype=float) yaw_angles = np.array(df_windrose[yaw_cols], dtype=float) - fi.reinitialize(wind_directions=wd_array, wind_speeds=ws_array) + fi.set(wind_directions=wd_array, wind_speeds=ws_array, yaw_angles=yaw_angles) # Calculate FLORIS for every WD and WS combination and get the farm power - fi.calculate_wake(yaw_angles) + fi.run() farm_power_array = fi.get_farm_power() # Now map FLORIS solutions to dataframe @@ -90,7 +90,7 @@ def calculate_aep(fi, df_windrose, column_name="farm_power"): # Load FLORIS fi = load_floris() ws_array = 8.0 * np.ones_like(fi.floris.flow_field.wind_directions) - fi.reinitialize(wind_speeds=ws_array) + fi.set(wind_speeds=ws_array) nturbs = len(fi.layout_x) # First, get baseline AEP, without wake steering @@ -109,7 +109,7 @@ def calculate_aep(fi, df_windrose, column_name="farm_power"): start_time = timerpc() wd_array = np.arange(0.0, 360.0, 5.0) ws_array = 8.0 * np.ones_like(wd_array) - fi.reinitialize( + fi.set( wind_directions=wd_array, wind_speeds=ws_array, ) diff --git a/examples/12_optimize_yaw_in_parallel.py b/examples/12_optimize_yaw_in_parallel.py index d46c94e0c..955c32e06 100644 --- a/examples/12_optimize_yaw_in_parallel.py +++ b/examples/12_optimize_yaw_in_parallel.py @@ -23,7 +23,7 @@ def load_floris(): 5.0 * fi.floris.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1), 5.0 * fi.floris.farm.rotor_diameters_sorted[0][0] * np.arange(0, N, 1), ) - fi.reinitialize(layout_x=X.flatten(), layout_y=Y.flatten()) + fi.set(layout_x=X.flatten(), layout_y=Y.flatten()) return fi @@ -59,7 +59,7 @@ def load_windrose(): wd_array = wind_directions_grid.flatten() ws_array = wind_speeds_grid.flatten() - fi_aep.reinitialize( + fi_aep.set( wind_directions=wd_array, wind_speeds=ws_array, turbulence_intensities=[0.08], # Assume 8% turbulence intensity @@ -112,7 +112,7 @@ def load_windrose(): wd_array_opt = wind_directions_grid.flatten() ws_array_opt = wind_speeds_grid.flatten() - fi_opt.reinitialize( + fi_opt.set( wind_directions=wd_array_opt, wind_speeds=ws_array_opt, turbulence_intensities=[0.08], # Assume 8% turbulence intensity diff --git a/examples/13_optimize_yaw_with_neighboring_farm.py b/examples/13_optimize_yaw_with_neighboring_farm.py index bd201717b..e388909c2 100644 --- a/examples/13_optimize_yaw_with_neighboring_farm.py +++ b/examples/13_optimize_yaw_with_neighboring_farm.py @@ -51,7 +51,7 @@ def load_floris(): turbine_weights[0:10] = 1.0 # Now reinitialize FLORIS layout - fi.reinitialize(layout_x = X, layout_y = Y) + fi.set(layout_x = X, layout_y = Y) # And visualize the floris layout fig, ax = plt.subplots() @@ -180,13 +180,13 @@ def yaw_opt_interpolant(wd, ws): # Create a FLORIS object for AEP calculations fi_AEP = fi.copy() - fi_AEP.reinitialize(wind_speeds=ws_windrose, wind_directions=wd_windrose) + fi_AEP.set(wind_speeds=ws_windrose, wind_directions=wd_windrose) # And create a separate FLORIS object for optimization fi_opt = fi.copy() wd_array = np.arange(0.0, 360.0, 3.0) ws_array = 8.0 * np.ones_like(wd_array) - fi_opt.reinitialize( + fi_opt.set( wind_directions=wd_array, wind_speeds=ws_array, ) @@ -222,7 +222,7 @@ def yaw_opt_interpolant(wd, ws): # Optimize yaw angles while ignoring neighboring farm fi_opt_subset = fi_opt.copy() - fi_opt_subset.reinitialize( + fi_opt_subset.set( layout_x = fi.layout_x[turbs_to_opt], layout_y = fi.layout_y[turbs_to_opt] ) @@ -239,15 +239,15 @@ def yaw_opt_interpolant(wd, ws): print(" ") print("===========================================================") print("Calculating annual energy production with wake steering (AEP)...") + fi_AEP.set(yaw_angles=yaw_angles_opt_nonb_AEP) aep_opt_subset_nonb = 1.0e-9 * fi_AEP.get_farm_AEP( freq=freq_windrose, turbine_weights=turbine_weights, - yaw_angles=yaw_angles_opt_nonb_AEP, ) + fi_AEP.set(yaw_angles=yaw_angles_opt_AEP) aep_opt_subset = 1.0e-9 * fi_AEP.get_farm_AEP( freq=freq_windrose, turbine_weights=turbine_weights, - yaw_angles=yaw_angles_opt_AEP, ) uplift_subset_nonb = 100.0 * (aep_opt_subset_nonb - aep_bl_subset) / aep_bl_subset uplift_subset = 100.0 * (aep_opt_subset - aep_bl_subset) / aep_bl_subset @@ -271,15 +271,18 @@ def yaw_opt_interpolant(wd, ws): yaw_angles_opt_nonb[:, turbs_to_opt] = yaw_opt_interpolant_nonb(wd, ws) fi_opt = fi_opt.copy() - fi_opt.calculate_wake(yaw_angles=np.zeros_like(yaw_angles_opt)) + fi_opt.set(yaw_angles=np.zeros_like(yaw_angles_opt)) + fi_opt.run() farm_power_bl_subset = fi_opt.get_farm_power(turbine_weights).flatten() fi_opt = fi_opt.copy() - fi_opt.calculate_wake(yaw_angles=yaw_angles_opt) + fi_opt.set(yaw_angles=yaw_angles_opt) + fi_opt.run() farm_power_opt_subset = fi_opt.get_farm_power(turbine_weights).flatten() fi_opt = fi_opt.copy() - fi_opt.calculate_wake(yaw_angles=yaw_angles_opt_nonb) + fi_opt.set(yaw_angles=yaw_angles_opt_nonb) + fi_opt.run() farm_power_opt_subset_nonb = fi_opt.get_farm_power(turbine_weights).flatten() fig, ax = plt.subplots() diff --git a/examples/14_compare_yaw_optimizers.py b/examples/14_compare_yaw_optimizers.py index 16d6d9767..98a3937b2 100644 --- a/examples/14_compare_yaw_optimizers.py +++ b/examples/14_compare_yaw_optimizers.py @@ -38,7 +38,7 @@ D = 126.0 # Rotor diameter for the NREL 5 MW wd_array = np.arange(0.0, 360.0, 3.0) ws_array = 8.0 * np.ones_like(wd_array) -fi.reinitialize( +fi.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, @@ -92,7 +92,8 @@ # Before plotting results, need to compute values for GEOOPT since it doesn't compute # power within the optimization -fi.calculate_wake(yaw_angles=yaw_angles_opt_geo) +fi.set(yaw_angles=yaw_angles_opt_geo) +fi.run() geo_farm_power = fi.get_farm_power().squeeze() diff --git a/examples/15_optimize_layout.py b/examples/15_optimize_layout.py index ee477ade5..f35a08a35 100644 --- a/examples/15_optimize_layout.py +++ b/examples/15_optimize_layout.py @@ -32,7 +32,7 @@ freq = (np.abs(np.sort(np.random.randn(len(wind_directions))))) freq = freq / freq.sum() -fi.reinitialize(wind_directions=wind_directions, wind_speeds=wind_speeds) +fi.set(wind_directions=wind_directions, wind_speeds=wind_speeds) # The boundaries for the turbines, specified as vertices boundaries = [(0.0, 0.0), (0.0, 1000.0), (1000.0, 1000.0), (1000.0, 0.0), (0.0, 0.0)] @@ -41,7 +41,7 @@ D = 126.0 # rotor diameter for the NREL 5MW layout_x = [0, 0, 6 * D, 6 * D] layout_y = [0, 4 * D, 0, 4 * D] -fi.reinitialize(layout_x=layout_x, layout_y=layout_y) +fi.set(layout_x=layout_x, layout_y=layout_y) # Setup the optimization problem layout_opt = LayoutOptimizationScipy(fi, boundaries, freq=freq) @@ -51,10 +51,10 @@ # Get the resulting improvement in AEP print('... calcuating improvement in AEP') -fi.calculate_wake() +fi.run() base_aep = fi.get_farm_AEP(freq=freq) / 1e6 -fi.reinitialize(layout_x=sol[0], layout_y=sol[1]) -fi.calculate_wake() +fi.set(layout_x=sol[0], layout_y=sol[1]) +fi.run() opt_aep = fi.get_farm_AEP(freq=freq) / 1e6 percent_gain = 100 * (opt_aep - base_aep) / base_aep diff --git a/examples/16_heterogeneous_inflow.py b/examples/16_heterogeneous_inflow.py index 2ac09ebf0..cc71b80c4 100644 --- a/examples/16_heterogeneous_inflow.py +++ b/examples/16_heterogeneous_inflow.py @@ -37,7 +37,7 @@ fi_2d = FlorisInterface("inputs/gch_heterogeneous_inflow.yaml") # Set shear to 0.0 to highlight the heterogeneous inflow -fi_2d.reinitialize(wind_shear=0.0) +fi_2d.set(wind_shear=0.0) # Using the FlorisInterface functions for generating plots, run FLORIS # and extract 2D planes of data. @@ -105,10 +105,10 @@ # Note that we initialize FLORIS with a homogenous flow input file, but # then configure the heterogeneous inflow via the reinitialize method. fi_3d = FlorisInterface("inputs/gch.yaml") -fi_3d.reinitialize(heterogenous_inflow_config=heterogenous_inflow_config) +fi_3d.set(heterogenous_inflow_config=heterogenous_inflow_config) # Set shear to 0.0 to highlight the heterogeneous inflow -fi_3d.reinitialize(wind_shear=0.0) +fi_3d.set(wind_shear=0.0) # Using the FlorisInterface functions for generating plots, run FLORIS # and extract 2D planes of data. diff --git a/examples/16b_heterogeneity_multiple_ws_wd.py b/examples/16b_heterogeneity_multiple_ws_wd.py index 46cd553a7..9fc662314 100644 --- a/examples/16b_heterogeneity_multiple_ws_wd.py +++ b/examples/16b_heterogeneity_multiple_ws_wd.py @@ -24,14 +24,14 @@ fi = FlorisInterface("inputs/gch_heterogeneous_inflow.yaml") # Set shear to 0.0 to highlight the heterogeneous inflow -fi.reinitialize( +fi.set( wind_shear=0.0, wind_speeds=[8.0], wind_directions=[270.], layout_x=[0, 0], layout_y=[-299., 299.], ) -fi.calculate_wake() +fi.run() turbine_powers = fi.get_turbine_powers().flatten() / 1000. # Show the initial results @@ -52,12 +52,12 @@ 'x': x_locs, 'y': y_locs, } -fi.reinitialize( +fi.set( wind_directions=[270.0, 275.0], wind_speeds=[8.0, 8.0], heterogenous_inflow_config=heterogenous_inflow_config ) -fi.calculate_wake() +fi.run() turbine_powers = np.round(fi.get_turbine_powers() / 1000.) print('With wind directions now set to 270 and 275 deg') print(f'T0: {turbine_powers[:, 0].flatten()} kW') @@ -69,6 +69,6 @@ # print() # print('~~ Now forcing an error by not matching wd and het_map') -# fi.reinitialize(wind_directions=[270, 275, 280], wind_speeds=3*[8.0]) -# fi.calculate_wake() +# fi.set(wind_directions=[270, 275, 280], wind_speeds=3*[8.0]) +# fi.run() # turbine_powers = np.round(fi.get_turbine_powers() / 1000.) diff --git a/examples/16c_optimize_layout_with_heterogeneity.py b/examples/16c_optimize_layout_with_heterogeneity.py index 1d30bd5e6..a618aaa1d 100644 --- a/examples/16c_optimize_layout_with_heterogeneity.py +++ b/examples/16c_optimize_layout_with_heterogeneity.py @@ -63,7 +63,7 @@ 'y': y_locs, } -fi.reinitialize( +fi.set( layout_x=layout_x, layout_y=layout_y, wind_directions=wind_directions, @@ -87,10 +87,10 @@ # Get the resulting improvement in AEP print('... calcuating improvement in AEP') -fi.calculate_wake() +fi.run() base_aep = fi.get_farm_AEP(freq=freq) / 1e6 -fi.reinitialize(layout_x=sol[0], layout_y=sol[1]) -fi.calculate_wake() +fi.set(layout_x=sol[0], layout_y=sol[1]) +fi.run() opt_aep = fi.get_farm_AEP(freq=freq) / 1e6 percent_gain = 100 * (opt_aep - base_aep) / base_aep @@ -111,7 +111,7 @@ # Rerun the layout optimization with geometric yaw enabled print("\nReoptimizing with geometric yaw enabled.") -fi.reinitialize(layout_x=layout_x, layout_y=layout_y) +fi.set(layout_x=layout_x, layout_y=layout_y) layout_opt = LayoutOptimizationScipy( fi, boundaries, @@ -127,11 +127,10 @@ # Get the resulting improvement in AEP print('... calcuating improvement in AEP') -fi.calculate_wake() +fi.set(yaw_angles=np.zeros_like(layout_opt.yaw_angles)) base_aep = fi.get_farm_AEP(freq=freq) / 1e6 -fi.reinitialize(layout_x=sol[0], layout_y=sol[1]) -fi.calculate_wake() -opt_aep = fi.get_farm_AEP(freq=freq, yaw_angles=layout_opt.yaw_angles) / 1e6 +fi.set(layout_x=sol[0], layout_y=sol[1], yaw_angles=layout_opt.yaw_angles) +opt_aep = fi.get_farm_AEP(freq=freq) / 1e6 percent_gain = 100 * (opt_aep - base_aep) / base_aep # Print and plot the results diff --git a/examples/18_check_turbine.py b/examples/18_check_turbine.py index 423c67e42..a19a99306 100644 --- a/examples/18_check_turbine.py +++ b/examples/18_check_turbine.py @@ -19,11 +19,11 @@ # Grab the gch model fi = FlorisInterface("inputs/gch.yaml") -# Make one turbine sim -fi.reinitialize(layout_x=[0], layout_y=[0]) +# Make one turbine simulation +fi.set(layout_x=[0], layout_y=[0]) -# Apply wind speeds -fi.reinitialize(wind_speeds=ws_array, wind_directions=wd_array) +# Apply wind directions and wind speeds +fi.set(wind_speeds=ws_array, wind_directions=wd_array) # Get a list of available turbine models provided through FLORIS, and remove # multi-dimensional Cp/Ct turbine definitions as they require different handling @@ -40,7 +40,7 @@ for t in turbines: # Set t as the turbine - fi.reinitialize(turbine_type=[t]) + fi.set(turbine_type=[t]) # Since we are changing the turbine type, make a matching change to the reference wind height fi.assign_hub_height_to_ref_height() @@ -68,12 +68,12 @@ # Try a few density for density in [1.15,1.225,1.3]: - fi.reinitialize(air_density=density) + fi.set(air_density=density) # POWER CURVE ax = axarr[0] - fi.reinitialize(wind_speeds=ws_array, wind_directions=wd_array) - fi.calculate_wake() + fi.set(wind_speeds=ws_array, wind_directions=wd_array) + fi.run() turbine_powers = fi.get_turbine_powers().flatten() / 1e3 if density == 1.225: ax.plot(ws_array,turbine_powers,label='Air Density = %.3f' % density, lw=2, color='k') @@ -87,10 +87,11 @@ # Power loss to yaw, try a range of yaw angles ax = axarr[1] - fi.reinitialize(wind_speeds=[wind_speed_to_test_yaw], wind_directions=[270.0]) + fi.set(wind_speeds=[wind_speed_to_test_yaw], wind_directions=[270.0]) yaw_result = [] for yaw in yaw_angles: - fi.calculate_wake(yaw_angles=np.array([[yaw]])) + fi.set(yaw_angles=np.array([[yaw]])) + fi.run() turbine_powers = fi.get_turbine_powers().flatten() / 1e3 yaw_result.append(turbine_powers[0]) if density == 1.225: diff --git a/examples/21_demo_time_series.py b/examples/21_demo_time_series.py index 7dfbf78a2..3c489ff45 100644 --- a/examples/21_demo_time_series.py +++ b/examples/21_demo_time_series.py @@ -14,7 +14,7 @@ fi = FlorisInterface("inputs/gch.yaml") # Convert to a simple two turbine layout -fi.reinitialize(layout_x=[0, 500.], layout_y=[0., 0.]) +fi.set(layout_x=[0, 500.], layout_y=[0., 0.]) # Create a fake time history where wind speed steps in the middle while wind direction # Walks randomly @@ -28,10 +28,10 @@ # Now intiialize FLORIS object to this history using time_series flag -fi.reinitialize(wind_directions=wd, wind_speeds=ws) +fi.set(wind_directions=wd, wind_speeds=ws) # Collect the powers -fi.calculate_wake() +fi.run() turbine_powers = fi.get_turbine_powers() / 1000. # Show the dimensions diff --git a/examples/22_get_wind_speed_at_turbines.py b/examples/22_get_wind_speed_at_turbines.py index 6eea39179..b5dfeb7d4 100644 --- a/examples/22_get_wind_speed_at_turbines.py +++ b/examples/22_get_wind_speed_at_turbines.py @@ -10,10 +10,10 @@ fi = FlorisInterface("inputs/gch.yaml") # Create a 4-turbine layouts -fi.reinitialize(layout_x=[0, 0., 500., 500.], layout_y=[0., 300., 0., 300.]) +fi.set(layout_x=[0, 0., 500., 500.], layout_y=[0., 300., 0., 300.]) # Calculate wake -fi.calculate_wake() +fi.run() # Collect the wind speed at all the turbine points u_points = fi.floris.flow_field.u diff --git a/examples/23_visualize_layout.py b/examples/23_visualize_layout.py index 9628ad7f9..b3cc39538 100644 --- a/examples/23_visualize_layout.py +++ b/examples/23_visualize_layout.py @@ -14,7 +14,7 @@ fi = FlorisInterface("inputs/gch.yaml") # Assign a 6-turbine layout -fi.reinitialize(layout_x=[0, 100, 500, 1000, 1200,500], layout_y=[0, 800, 150, 500, 0,500]) +fi.set(layout_x=[0, 100, 500, 1000, 1200,500], layout_y=[0, 800, 150, 500, 0,500]) # Give turbines specific names turbine_names = ['T01', 'T02','T03','S01','X01', 'X02'] diff --git a/examples/24_floating_turbine_models.py b/examples/24_floating_turbine_models.py index 12f731816..db586608f 100644 --- a/examples/24_floating_turbine_models.py +++ b/examples/24_floating_turbine_models.py @@ -40,13 +40,13 @@ # Calculate across wind speeds ws_array = np.arange(3., 25., 1.) wd_array = 270.0 * np.ones_like(ws_array) -fi_fixed.reinitialize(wind_speeds=ws_array, wind_directions=wd_array) -fi_floating.reinitialize(wind_speeds=ws_array, wind_directions=wd_array) -fi_floating_defined_floating.reinitialize(wind_speeds=ws_array, wind_directions=wd_array) +fi_fixed.set(wind_speeds=ws_array, wind_directions=wd_array) +fi_floating.set(wind_speeds=ws_array, wind_directions=wd_array) +fi_floating_defined_floating.set(wind_speeds=ws_array, wind_directions=wd_array) -fi_fixed.calculate_wake() -fi_floating.calculate_wake() -fi_floating_defined_floating.calculate_wake() +fi_fixed.run() +fi_floating.run() +fi_floating_defined_floating.run() # Grab power power_fixed = fi_fixed.get_turbine_powers().flatten()/1000. diff --git a/examples/25_tilt_driven_vertical_wake_deflection.py b/examples/25_tilt_driven_vertical_wake_deflection.py index 69a05ac91..05575a40f 100644 --- a/examples/25_tilt_driven_vertical_wake_deflection.py +++ b/examples/25_tilt_driven_vertical_wake_deflection.py @@ -49,7 +49,7 @@ for i, (fi, tilt) in enumerate(zip([fi_5, fi_15], [5, 15])): # Farm layout and wind conditions - fi.reinitialize( + fi.set( layout_x=[x * 5.0 * D for x in range(num_in_row)], layout_y=[0.0]*num_in_row, wind_speeds=[8.0], @@ -57,7 +57,7 @@ ) # Flow solve and power computation - fi.calculate_wake() + fi.run() powers[i,:] = fi.get_turbine_powers().flatten() # Compute flow slices diff --git a/examples/26_empirical_gauss_velocity_deficit_parameters.py b/examples/26_empirical_gauss_velocity_deficit_parameters.py index 1b48f8543..2dc5bb43e 100644 --- a/examples/26_empirical_gauss_velocity_deficit_parameters.py +++ b/examples/26_empirical_gauss_velocity_deficit_parameters.py @@ -103,7 +103,7 @@ def generate_wake_visualization(fi: FlorisInterface, title=None): # Load input yaml and define farm layout fi = FlorisInterface("inputs/emgauss.yaml") D = fi.floris.farm.rotor_diameters[0] -fi.reinitialize( +fi.set( layout_x=[x*5.0*D for x in range(num_in_row)], layout_y=[0.0]*num_in_row, wind_speeds=[8.0], @@ -114,7 +114,7 @@ def generate_wake_visualization(fi: FlorisInterface, title=None): fi_dict = fi.floris.as_dict() # Run wake calculation -fi.calculate_wake() +fi.run() # Look at the powers of each turbine turbine_powers = fi.get_turbine_powers().flatten()/1e6 @@ -138,12 +138,12 @@ def generate_wake_visualization(fi: FlorisInterface, title=None): fi_dict_mod['wake']['wake_velocity_parameters']['empirical_gauss']\ ['wake_expansion_rates'] = [0.03, 0.015] fi = FlorisInterface(fi_dict_mod) -fi.reinitialize( +fi.set( wind_speeds=[8.0], wind_directions=[270.0] ) -fi.calculate_wake() +fi.run() turbine_powers = fi.get_turbine_powers().flatten()/1e6 x = np.array(range(num_in_row))+width*nw @@ -165,12 +165,12 @@ def generate_wake_visualization(fi: FlorisInterface, title=None): ['breakpoints_D'] = [5, 10] fi = FlorisInterface(fi_dict_mod) -fi.reinitialize( +fi.set( wind_speeds=[8.0], wind_directions=[270.0] ) -fi.calculate_wake() +fi.run() turbine_powers = fi.get_turbine_powers().flatten()/1e6 x = np.array(range(num_in_row))+width*nw @@ -187,12 +187,12 @@ def generate_wake_visualization(fi: FlorisInterface, title=None): fi_dict_mod['wake']['wake_velocity_parameters']['empirical_gauss']\ ['mixing_gain_velocity'] = 3.0 fi = FlorisInterface(fi_dict_mod) -fi.reinitialize( +fi.set( wind_speeds=[8.0], wind_directions=[270.0] ) -fi.calculate_wake() +fi.run() turbine_powers = fi.get_turbine_powers().flatten()/1e6 x = np.array(range(num_in_row))+width*nw diff --git a/examples/27_empirical_gauss_deflection_parameters.py b/examples/27_empirical_gauss_deflection_parameters.py index 1b0095a23..5a24aaec7 100644 --- a/examples/27_empirical_gauss_deflection_parameters.py +++ b/examples/27_empirical_gauss_deflection_parameters.py @@ -107,18 +107,19 @@ def generate_wake_visualization(fi, title=None): # Load input yaml and define farm layout fi = FlorisInterface("inputs/emgauss.yaml") D = fi.floris.farm.rotor_diameters[0] -fi.reinitialize( +fi.set( layout_x=[x*5.0*D for x in range(num_in_row)], layout_y=[0.0]*num_in_row, wind_speeds=[8.0], - wind_directions=[270.0] + wind_directions=[270.0], + yaw_angles=yaw_angles, ) # Save dictionary to modify later fi_dict = fi.floris.as_dict() # Run wake calculation -fi.calculate_wake(yaw_angles=yaw_angles) +fi.run() # Look at the powers of each turbine turbine_powers = fi.get_turbine_powers().flatten()/1e6 @@ -144,12 +145,13 @@ def generate_wake_visualization(fi, title=None): ['horizontal_deflection_gain_D'] = 5.0 fi = FlorisInterface(fi_dict_mod) -fi.reinitialize( +fi.set( wind_speeds=[8.0], - wind_directions=[270.0] + wind_directions=[270.0], + yaw_angles=yaw_angles, ) -fi.calculate_wake(yaw_angles=yaw_angles) +fi.run() turbine_powers = fi.get_turbine_powers().flatten()/1e6 x = np.array(range(num_in_row))+width*nw @@ -167,12 +169,13 @@ def generate_wake_visualization(fi, title=None): ['mixing_gain_deflection'] = 100.0 fi = FlorisInterface(fi_dict_mod) -fi.reinitialize( +fi.set( wind_speeds=[8.0], - wind_directions=[270.0] + wind_directions=[270.0], + yaw_angles=yaw_angles, ) -fi.calculate_wake(yaw_angles=yaw_angles) +fi.run() turbine_powers = fi.get_turbine_powers().flatten()/1e6 x = np.array(range(num_in_row))+width*nw @@ -193,12 +196,13 @@ def generate_wake_visualization(fi, title=None): fi_dict_mod['wake']['wake_deflection_parameters']['empirical_gauss']\ ['yaw_added_mixing_gain'] = 1.0 fi = FlorisInterface(fi_dict_mod) -fi.reinitialize( +fi.set( wind_speeds=[8.0], - wind_directions=[270.0] + wind_directions=[270.0], + yaw_angles=yaw_angles, ) -fi.calculate_wake(yaw_angles=yaw_angles) +fi.run() turbine_powers = fi.get_turbine_powers().flatten()/1e6 x = np.array(range(num_in_row))+width*nw diff --git a/examples/28_extract_wind_speed_at_points.py b/examples/28_extract_wind_speed_at_points.py index 04ef2daa5..6e68b988b 100644 --- a/examples/28_extract_wind_speed_at_points.py +++ b/examples/28_extract_wind_speed_at_points.py @@ -30,7 +30,7 @@ # Set up a two-turbine farm D = 126 -fi.reinitialize(layout_x=[0, 3 * D], layout_y=[0, 3 * D]) +fi.set(layout_x=[0, 3 * D], layout_y=[0, 3 * D]) fig, ax = plt.subplots(1,2) fig.set_size_inches(10,4) @@ -39,7 +39,7 @@ # Set the wind direction to run 360 degrees wd_array = np.arange(0, 360, 1) ws_array = 8.0 * np.ones_like(wd_array) -fi.reinitialize(wind_directions=wd_array, wind_speeds=ws_array) +fi.set(wind_directions=wd_array, wind_speeds=ws_array) # Simulate a met mast in between the turbines if met_mast_option == 0: diff --git a/examples/29_floating_vs_fixedbottom_farm.py b/examples/29_floating_vs_fixedbottom_farm.py index a6fc380a1..e141144aa 100644 --- a/examples/29_floating_vs_fixedbottom_farm.py +++ b/examples/29_floating_vs_fixedbottom_farm.py @@ -38,17 +38,17 @@ x = x.flatten() y = y.flatten() for fi in [fi_fixed, fi_floating]: - fi.reinitialize(layout_x=x, layout_y=y) + fi.set(layout_x=x, layout_y=y) # Compute a single wind speed and direction, power and wakes for fi in [fi_fixed, fi_floating]: - fi.reinitialize( + fi.set( layout_x=x, layout_y=y, wind_speeds=[10], wind_directions=[270] ) - fi.calculate_wake() + fi.run() powers_fixed = fi_fixed.get_turbine_powers() powers_floating = fi_floating.get_turbine_powers() @@ -118,7 +118,7 @@ freq = freq / np.sum(freq) for fi in [fi_fixed, fi_floating]: - fi.reinitialize( + fi.set( wind_directions=wd_grid.flatten(), wind_speeds= ws_grid.flatten(), ) diff --git a/examples/30_multi_dimensional_cp_ct.py b/examples/30_multi_dimensional_cp_ct.py index 429159a0b..af28d6500 100644 --- a/examples/30_multi_dimensional_cp_ct.py +++ b/examples/30_multi_dimensional_cp_ct.py @@ -44,17 +44,20 @@ fi = FlorisInterface("inputs/gch_multi_dim_cp_ct.yaml") # Convert to a simple two turbine layout -fi.reinitialize(layout_x=[0., 500.], layout_y=[0., 0.]) +fi.set(layout_x=[0., 500.], layout_y=[0., 0.]) # Single wind speed and wind direction print('\n========================= Single Wind Direction and Wind Speed =========================') # Get the turbine powers assuming 1 wind speed and 1 wind direction -fi.reinitialize(wind_directions=[270.0], wind_speeds=[8.0]) +fi.set(wind_directions=[270.0], wind_speeds=[8.0]) # Set the yaw angles to 0 yaw_angles = np.zeros([1, 2]) # 1 wind direction and wind speed, 2 turbines -fi.calculate_wake(yaw_angles=yaw_angles) +fi.set(yaw_angles=yaw_angles) + +# Calculate +fi.run() # Get the turbine powers turbine_powers = fi.get_turbine_powers() / 1000.0 @@ -68,9 +71,9 @@ wind_speeds = np.array([8.0, 9.0, 10.0]) wind_directions = np.array([270.0, 270.0, 270.0]) -fi.reinitialize(wind_speeds=wind_speeds, wind_directions=wind_directions) yaw_angles = np.zeros([3, 2]) # 3 wind directions/ speeds, 2 turbines -fi.calculate_wake(yaw_angles=yaw_angles) +fi.set(wind_speeds=wind_speeds, wind_directions=wind_directions, yaw_angles=yaw_angles) +fi.run() turbine_powers = fi.get_turbine_powers() / 1000.0 print("The turbine power matrix should be of dimensions 3 findex X 2 Turbines") print(turbine_powers) @@ -82,9 +85,9 @@ wind_speeds = np.tile([8.0, 9.0, 10.0], 3) wind_directions = np.repeat([260.0, 270.0, 280.0], 3) -fi.reinitialize(wind_directions=wind_directions, wind_speeds=wind_speeds) yaw_angles = np.zeros([9, 2]) # 9 wind directions/ speeds, 2 turbines -fi.calculate_wake(yaw_angles=yaw_angles) +fi.set(wind_directions=wind_directions, wind_speeds=wind_speeds, yaw_angles=yaw_angles) +fi.run() turbine_powers = fi.get_turbine_powers()/1000. print("The turbine power matrix should be of dimensions 9 WD/WS X 2 Turbines") print(turbine_powers) diff --git a/examples/31_multi_dimensional_cp_ct_2Hs.py b/examples/31_multi_dimensional_cp_ct_2Hs.py index 032df5fa9..b61fcb0f0 100644 --- a/examples/31_multi_dimensional_cp_ct_2Hs.py +++ b/examples/31_multi_dimensional_cp_ct_2Hs.py @@ -28,18 +28,18 @@ fi_hs_1 = FlorisInterface(fi_dict_mod) # Set both cases to 3 turbine layout -fi.reinitialize(layout_x=[0., 500., 1000.], layout_y=[0., 0., 0.]) -fi_hs_1.reinitialize(layout_x=[0., 500., 1000.], layout_y=[0., 0., 0.]) +fi.set(layout_x=[0., 500., 1000.], layout_y=[0., 0., 0.]) +fi_hs_1.set(layout_x=[0., 500., 1000.], layout_y=[0., 0., 0.]) # Use a sweep of wind speeds wind_speeds = np.arange(5, 20, 1.0) wind_directions = 270.0 * np.ones_like(wind_speeds) -fi.reinitialize(wind_directions=wind_directions, wind_speeds=wind_speeds) -fi_hs_1.reinitialize(wind_directions=wind_directions, wind_speeds=wind_speeds) +fi.set(wind_directions=wind_directions, wind_speeds=wind_speeds) +fi_hs_1.set(wind_directions=wind_directions, wind_speeds=wind_speeds) # Calculate wakes with baseline yaw -fi.calculate_wake() -fi_hs_1.calculate_wake() +fi.run() +fi_hs_1.run() # Collect the turbine powers in kW turbine_powers = fi.get_turbine_powers()/1000. diff --git a/examples/32_plot_velocity_deficit_profiles.py b/examples/32_plot_velocity_deficit_profiles.py index a556a666c..9f28ce40c 100644 --- a/examples/32_plot_velocity_deficit_profiles.py +++ b/examples/32_plot_velocity_deficit_profiles.py @@ -55,7 +55,7 @@ def annotate_coordinate_system(x_origin, y_origin, quiver_length): homogeneous_wind_speed = 8.0 fi = FlorisInterface("inputs/gch.yaml") - fi.reinitialize(layout_x=[0.0], layout_y=[0.0]) + fi.set(layout_x=[0.0], layout_y=[0.0]) # ------------------------------ Single-turbine layout ------------------------------ # We first show how to sample and plot velocity deficit profiles on a single-turbine layout. @@ -131,7 +131,7 @@ def annotate_coordinate_system(x_origin, y_origin, quiver_length): # Let (x_t1, y_t1) be the location of the second turbine x_t1 = 2 * D y_t1 = -2 * D - fi.reinitialize(wind_directions=[wind_direction], layout_x=[0.0, x_t1], layout_y=[0.0, y_t1]) + fi.set(wind_directions=[wind_direction], layout_x=[0.0, x_t1], layout_y=[0.0, y_t1]) # Extract profiles at a set of downstream distances from the starting point (x_start, y_start) cross_profiles = fi.sample_velocity_deficit_profiles( diff --git a/examples/33_specify_turbine_power_curve.py b/examples/33_specify_turbine_power_curve.py index 2359cebb8..6b2c2f4b2 100644 --- a/examples/33_specify_turbine_power_curve.py +++ b/examples/33_specify_turbine_power_curve.py @@ -43,14 +43,14 @@ wind_speeds = np.linspace(1, 15, 100) wind_directions = 270 * np.ones_like(wind_speeds) # Replace the turbine(s) in the FLORIS model with the created one -fi.reinitialize( +fi.set( layout_x=[0], layout_y=[0], wind_directions=wind_directions, wind_speeds=wind_speeds, turbine_type=[turbine_dict] ) -fi.calculate_wake() +fi.run() powers = fi.get_farm_power() diff --git a/examples/34_wind_data.py b/examples/34_wind_data.py index 44a40a99d..79469c988 100644 --- a/examples/34_wind_data.py +++ b/examples/34_wind_data.py @@ -60,19 +60,19 @@ # Now set up a FLORIS model and initialize it using the time series and wind rose fi = FlorisInterface("inputs/gch.yaml") -fi.reinitialize(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) +fi.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0]) fi_time_series = fi.copy() fi_wind_rose = fi.copy() fi_wind_ti_rose = fi.copy() -fi_time_series.reinitialize(wind_data=time_series) -fi_wind_rose.reinitialize(wind_data=wind_rose) -fi_wind_ti_rose.reinitialize(wind_data=wind_ti_rose) +fi_time_series.set(wind_data=time_series) +fi_wind_rose.set(wind_data=wind_rose) +fi_wind_ti_rose.set(wind_data=wind_ti_rose) -fi_time_series.calculate_wake() -fi_wind_rose.calculate_wake() -fi_wind_ti_rose.calculate_wake() +fi_time_series.run() +fi_wind_rose.run() +fi_wind_ti_rose.run() time_series_power = fi_time_series.get_farm_power() wind_rose_power = fi_wind_rose.get_farm_power() diff --git a/examples/35_sweep_ti.py b/examples/35_sweep_ti.py index 471a9cb67..23942150e 100644 --- a/examples/35_sweep_ti.py +++ b/examples/35_sweep_ti.py @@ -30,8 +30,8 @@ # Now set up a FLORIS model and initialize it using the time fi = FlorisInterface("inputs/gch.yaml") -fi.reinitialize(layout_x=[0, 500.0], layout_y=[0.0, 0.0], wind_data=time_series) -fi.calculate_wake() +fi.set(layout_x=[0, 500.0], layout_y=[0.0, 0.0], wind_data=time_series) +fi.run() turbine_power = fi.get_turbine_powers() fig, axarr = plt.subplots(2, 1, sharex=True, figsize=(6, 6)) diff --git a/examples/40_test_derating.py b/examples/40_test_derating.py index 542e7963e..7f7f091f3 100644 --- a/examples/40_test_derating.py +++ b/examples/40_test_derating.py @@ -22,22 +22,24 @@ 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]) +fi.set(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() +fi.set(wind_directions=270 * np.ones(N), wind_speeds=10.0 * np.ones(N)) +fi.run() 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) +fi.set(power_setpoints=power_setpoints) +fi.run() 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) +fi.set(power_setpoints=power_setpoints_2) +fi.run() turbine_powers_avail_ds = fi.get_turbine_powers()[:,1] # Plot the results @@ -91,12 +93,14 @@ [2e6, None,], [None, 1e6] ]) -fi.reinitialize( +fi.set( wind_directions=270 * np.ones(len(yaw_angles)), wind_speeds=10.0 * np.ones(len(yaw_angles)), - turbine_type=[turbine_type]*2 + turbine_type=[turbine_type]*2, + yaw_angles=yaw_angles, + power_setpoints=power_setpoints, ) -fi.calculate_wake(yaw_angles=yaw_angles, power_setpoints=power_setpoints) +fi.run() turbine_powers = fi.get_turbine_powers() print(turbine_powers) diff --git a/examples/41_test_disable_turbines.py b/examples/41_test_disable_turbines.py index d276d8ce1..da514e224 100644 --- a/examples/41_test_disable_turbines.py +++ b/examples/41_test_disable_turbines.py @@ -24,7 +24,7 @@ ) as t: turbine_type = yaml.safe_load(t) turbine_type["power_thrust_model"] = "mixed" -fi.reinitialize(turbine_type=[turbine_type]) +fi.set(turbine_type=[turbine_type]) # Consider a wind farm of 3 aligned wind turbines layout = np.array([[0.0, 0.0], [500.0, 0.0], [1000.0, 0.0]]) @@ -42,15 +42,16 @@ # ------------------------------------------ # Reinitialize flow field -fi.reinitialize( +fi.set( layout_x=layout[:, 0], layout_y=layout[:, 1], wind_directions=wind_directions, wind_speeds=wind_speeds, + disable_turbines=disable_turbines, ) # # Compute wakes -fi.calculate_wake(disable_turbines=disable_turbines) +fi.run() # Results # ------------------------------------------ diff --git a/floris/simulation/flow_field.py b/floris/simulation/flow_field.py index 7de465da5..364462119 100644 --- a/floris/simulation/flow_field.py +++ b/floris/simulation/flow_field.py @@ -61,7 +61,7 @@ def turbulence_intensities_validator( # Check that the array is 1-dimensional if value.ndim != 1: raise ValueError( - "wind_directions must have 1-dimension" + "turbulence_intensities must have 1-dimension" ) # Check the turbulence intensity is either length 1 or n_findex @@ -90,7 +90,7 @@ def wind_speeds_validator(self, instance: attrs.Attribute, value: NDArrayFloat) "wind_speeds must have 1-dimension" ) - """Confirm wind speeds and wind directions have the same lenght""" + """Confirm wind speeds and wind directions have the same length""" if len(self.wind_directions) != len(self.wind_speeds): raise ValueError( f"wind_directions (length = {len(self.wind_directions)}) and " diff --git a/floris/simulation/turbine/operation_models.py b/floris/simulation/turbine/operation_models.py index dc12865fb..3d7a2b8e6 100644 --- a/floris/simulation/turbine/operation_models.py +++ b/floris/simulation/turbine/operation_models.py @@ -28,6 +28,7 @@ POWER_SETPOINT_DEFAULT = 1e12 +POWER_SETPOINT_DISABLED = 0.001 def rotor_velocity_air_density_correction( velocities: NDArrayFloat, diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index e9c5aa2f5..97d8cae4b 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -10,7 +10,10 @@ 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.operation_models import ( + POWER_SETPOINT_DEFAULT, + POWER_SETPOINT_DISABLED, +) from floris.simulation.turbine.turbine import ( axial_induction, power, @@ -108,175 +111,103 @@ def copy(self): """Create an independent copy of the current FlorisInterface object""" return FlorisInterface(self.floris.as_dict()) - def calculate_wake( + def set( self, + wind_speeds: list[float] | NDArrayFloat | None = None, + wind_directions: list[float] | NDArrayFloat | None = None, + wind_shear: float | None = None, + wind_veer: float | None = None, + reference_wind_height: float | None = None, + turbulence_intensities: list[float] | NDArrayFloat | None = None, + air_density: float | None = None, + layout_x: list[float] | NDArrayFloat | None = None, + layout_y: list[float] | NDArrayFloat | None = None, + turbine_type: list | None = None, + turbine_library_path: str | Path | None = None, + solver_settings: dict | None = None, + heterogenous_inflow_config=None, + wind_data: type[WindDataBase] | None = None, yaw_angles: NDArrayFloat | list[float] | None = None, - # tilt_angles: NDArrayFloat | list[float] | None = None, power_setpoints: NDArrayFloat | list[float] | list[float, None] | None = None, disable_turbines: NDArrayBool | list[bool] | None = None, - ) -> None: + ): """ - Wrapper to the :py:meth:`~.Farm.set_yaw_angles` and - :py:meth:`~.FlowField.calculate_wake` methods. + Set the wind conditions and operation setpoints for the wind farm. Args: + wind_speeds (NDArrayFloat | list[float] | None, optional): Wind speeds at each findex. + Defaults to None. + wind_directions (NDArrayFloat | list[float] | None, optional): Wind directions at each + findex. Defaults to None. + wind_shear (float | None, optional): Wind shear exponent. Defaults to None. + wind_veer (float | None, optional): Wind veer. Defaults to None. + reference_wind_height (float | None, optional): Reference wind height. Defaults to None. + turbulence_intensities (NDArrayFloat | list[float] | None, optional): Turbulence + intensities at each findex. Defaults to None. + air_density (float | None, optional): Air density. Defaults to None. + layout_x (NDArrayFloat | list[float] | None, optional): X-coordinates of the turbines. + Defaults to None. + layout_y (NDArrayFloat | list[float] | None, optional): Y-coordinates of the turbines. + Defaults to None. + turbine_type (list | None, optional): Turbine type. Defaults to None. + turbine_library_path (str | Path | None, optional): Path to the turbine library. + Defaults to None. + solver_settings (dict | None, optional): Solver settings. Defaults to None. + heterogenous_inflow_config (None, optional): Heterogenous inflow configuration. Defaults + to None. + wind_data (type[WindDataBase] | None, optional): Wind data. Defaults to None. 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. + power_setpoints (NDArrayFloat | list[float] | list[float, None] | None, optional): + Turbine power setpoints. disable_turbines (NDArrayBool | list[bool] | None, optional): NDArray with dimensions - n_findex x n_turbines. True values indicate the turbine is disabled at that findex - and the power setpoint at that position is set to 0. Defaults to None + n_findex x n_turbines. True values indicate the turbine is disabled at that findex + and the power setpoint at that position is set to 0. Defaults to None. """ + # Initialize a new Floris object after saving the setpoints + _yaw_angles = self.floris.farm.yaw_angles + _power_setpoints = self.floris.farm.power_setpoints + self._reinitialize( + wind_speeds=wind_speeds, + wind_directions=wind_directions, + wind_shear=wind_shear, + wind_veer=wind_veer, + reference_wind_height=reference_wind_height, + turbulence_intensities=turbulence_intensities, + air_density=air_density, + layout_x=layout_x, + layout_y=layout_y, + turbine_type=turbine_type, + turbine_library_path=turbine_library_path, + solver_settings=solver_settings, + heterogenous_inflow_config=heterogenous_inflow_config, + wind_data=wind_data, + ) - if yaw_angles is None: - yaw_angles = np.zeros( - ( - self.floris.flow_field.n_findex, - self.floris.farm.n_turbines, - ) - ) - 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) - - # Check for turbines to disable - if disable_turbines is not None: - - # Force to numpy array - disable_turbines = np.array(disable_turbines) - - # Must have first dimension = n_findex - if disable_turbines.shape[0] != self.floris.flow_field.n_findex: - raise ValueError( - f"disable_turbines has a size of {disable_turbines.shape[0]} " - f"in the 0th dimension, must be equal to " - f"n_findex={self.floris.flow_field.n_findex}" - ) - - # Must have first dimension = n_turbines - if disable_turbines.shape[1] != self.floris.farm.n_turbines: - raise ValueError( - f"disable_turbines has a size of {disable_turbines.shape[1]} " - f"in the 1th dimension, must be equal to " - f"n_turbines={self.floris.farm.n_turbines}" - ) - - # Set power_setpoints and yaw_angles to 0 in all locations where - # disable_turbines is True - yaw_angles[disable_turbines] = 0.0 - power_setpoints[disable_turbines] = 0.001 # Not zero to avoid numerical problems - - self.floris.farm.power_setpoints = power_setpoints - - # # TODO is this required? - # if tilt_angles is not None: - # self.floris.farm.tilt_angles = tilt_angles - # else: - # self.floris.farm.set_tilt_to_ref_tilt( - # self.floris.flow_field.n_findex, - # ) - - # Initialize solution space - self.floris.initialize_domain() - - # Perform the wake calculations - self.floris.steady_state_atmospheric_condition() + # If the yaw angles or power setpoints are not the default, set them back to the + # previous setting + if not (_yaw_angles == 0).all(): + self.floris.farm.yaw_angles = _yaw_angles + if not ( + (_power_setpoints == POWER_SETPOINT_DEFAULT) + | (_power_setpoints == POWER_SETPOINT_DISABLED) + ).all(): + self.floris.farm.power_setpoints = _power_setpoints + + # Set the operation + self._set_operation( + yaw_angles=yaw_angles, + power_setpoints=power_setpoints, + disable_turbines=disable_turbines, + ) - def calculate_no_wake( - self, - yaw_angles: NDArrayFloat | list[float] | None = None, - power_setpoints: NDArrayFloat | list[float] | list[float, None] | None = None, - disable_turbines: NDArrayBool | list[bool] | None = None, - ) -> None: + def reset_operation(self): """ - This function is similar to `calculate_wake()` except - that it does not apply a wake model. That is, the wind - farm is modeled as if there is no wake in the flow. - Yaw angles are used to reduce the power and thrust of - the turbine that is yawed. - - Args: - yaw_angles (NDArrayFloat | list[float] | None, optional): Turbine yaw angles. - Defaults to None. + Instantiate a new Floris object to set all operation setpoints to their default values. """ + self._reinitialize() - if yaw_angles is None: - yaw_angles = np.zeros( - ( - self.floris.flow_field.n_findex, - self.floris.farm.n_turbines, - ) - ) - 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) - - # Check for turbines to disable - if disable_turbines is not None: - - # Force to numpy array - # disable_turbines = np.array(disable_turbines) - - # Must have first dimension = n_findex - if disable_turbines.shape[0] != self.floris.flow_field.n_findex: - raise ValueError( - f"disable_turbines has a size of {disable_turbines.shape[0]} " - f"in the 0th dimension, must be equal to " - f"n_findex={self.floris.flow_field.n_findex}" - ) - - # Must have first dimension = n_turbines - if disable_turbines.shape[1] != self.floris.farm.n_turbines: - raise ValueError( - f"disable_turbines has a size of {disable_turbines.shape[1]} " - f"in the 1th dimension, must be equal to " - f"n_turbines={self.floris.farm.n_turbines}" - ) - - # Set power_setpoints and yaw_angles to 0 in all locations where - # disable_turbines is True - yaw_angles[disable_turbines] = 0.0 - power_setpoints[disable_turbines] = 0.001 # Not zero to avoid numerical problems - - self.floris.farm.power_setpoints = power_setpoints - - # Initialize solution space - self.floris.initialize_domain() - - # Finalize values to user-supplied order - self.floris.finalize() - - def reinitialize( + def _reinitialize( self, wind_speeds: list[float] | NDArrayFloat | None = None, wind_directions: list[float] | NDArrayFloat | None = None, @@ -284,9 +215,7 @@ def reinitialize( wind_veer: float | None = None, reference_wind_height: float | None = None, turbulence_intensities: list[float] | NDArrayFloat | None = None, - # turbulence_kinetic_energy=None, air_density: float | None = None, - # wake: WakeModelManager = None, layout_x: list[float] | NDArrayFloat | None = None, layout_y: list[float] | NDArrayFloat | None = None, turbine_type: list | None = None, @@ -295,6 +224,33 @@ def reinitialize( heterogenous_inflow_config=None, wind_data: type[WindDataBase] | None = None, ): + """ + Instantiate a new Floris object with updated conditions set by arguments. Any parameters + in Floris that aren't changed by arguments to this function retain their values. + + Args: + wind_speeds (NDArrayFloat | list[float] | None, optional): Wind speeds at each findex. + Defaults to None. + wind_directions (NDArrayFloat | list[float] | None, optional): Wind directions at each + findex. Defaults to None. + wind_shear (float | None, optional): Wind shear exponent. Defaults to None. + wind_veer (float | None, optional): Wind veer. Defaults to None. + reference_wind_height (float | None, optional): Reference wind height. Defaults to None. + turbulence_intensities (NDArrayFloat | list[float] | None, optional): Turbulence + intensities at each findex. Defaults to None. + air_density (float | None, optional): Air density. Defaults to None. + layout_x (NDArrayFloat | list[float] | None, optional): X-coordinates of the turbines. + Defaults to None. + layout_y (NDArrayFloat | list[float] | None, optional): Y-coordinates of the turbines. + Defaults to None. + turbine_type (list | None, optional): Turbine type. Defaults to None. + turbine_library_path (str | Path | None, optional): Path to the turbine library. + Defaults to None. + solver_settings (dict | None, optional): Solver settings. Defaults to None. + heterogenous_inflow_config (None, optional): Heterogenous inflow configuration. Defaults + to None. + wind_data (type[WindDataBase] | None, optional): Wind data. Defaults to None. + """ # Export the floris object recursively as a dictionary floris_dict = self.floris.as_dict() flow_field_dict = floris_dict["flow_field"] @@ -371,12 +327,6 @@ def reinitialize( if turbine_library_path is not None: farm_dict["turbine_library_path"] = turbine_library_path - ## Wake - # if wake is not None: - # self.floris.wake = wake - # if turbulence_kinetic_energy is not None: - # pass # TODO: not needed until GCH - if solver_settings is not None: floris_dict["solver"] = solver_settings @@ -386,6 +336,89 @@ def reinitialize( # Create a new instance of floris and attach to self self.floris = Floris.from_dict(floris_dict) + def _set_operation( + self, + yaw_angles: NDArrayFloat | list[float] | None = None, + power_setpoints: NDArrayFloat | list[float] | list[float, None] | None = None, + disable_turbines: NDArrayBool | list[bool] | None = None, + ): + """ + Apply operating setpoints to the floris object. + + Args: + yaw_angles (NDArrayFloat | list[float] | None, optional): Turbine yaw angles. Defaults + to None. + power_setpoints (NDArrayFloat | list[float] | list[float, None] | None, optional): + Turbine power setpoints. Defaults to None. + disable_turbines (NDArrayBool | list[bool] | None, optional): Boolean array on whether + to disable turbines. Defaults to None. + """ + # Add operating conditions to the floris object + if yaw_angles is not None: + self.floris.farm.yaw_angles = yaw_angles + + if power_setpoints is not None: + 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 + + # Check for turbines to disable + if disable_turbines is not None: + + # Force to numpy array + disable_turbines = np.array(disable_turbines) + + # Must have first dimension = n_findex + if disable_turbines.shape[0] != self.floris.flow_field.n_findex: + raise ValueError( + f"disable_turbines has a size of {disable_turbines.shape[0]} " + f"in the 0th dimension, must be equal to " + f"n_findex={self.floris.flow_field.n_findex}" + ) + + # Must have first dimension = n_turbines + if disable_turbines.shape[1] != self.floris.farm.n_turbines: + raise ValueError( + f"disable_turbines has a size of {disable_turbines.shape[1]} " + f"in the 1th dimension, must be equal to " + f"n_turbines={self.floris.farm.n_turbines}" + ) + + # Set power setpoints to small value (non zero to avoid numerical issues) and + # yaw_angles to 0 in all locations where disable_turbines is True + self.floris.farm.yaw_angles[disable_turbines] = 0.0 + self.floris.farm.power_setpoints[disable_turbines] = POWER_SETPOINT_DISABLED + + def run(self) -> None: + """ + Run the FLORIS solve to compute the velocity field and wake effects. + """ + + # Initialize solution space + self.floris.initialize_domain() + + # Perform the wake calculations + self.floris.steady_state_atmospheric_condition() + + def run_no_wake(self) -> None: + """ + This function is similar to `run()` except that it does not apply a wake model. That is, + the wind farm is modeled as if there is no wake in the flow. Operation settings may + reduce the power and thrust of the turbine to where they're applied. + """ + + # Initialize solution space + self.floris.initialize_domain() + + # Finalize values to user-supplied order + self.floris.finalize() + def get_plane_of_points( self, normal_vector="z", @@ -477,6 +510,8 @@ def calculate_horizontal_plane( wd=None, ws=None, yaw_angles=None, + power_setpoints=None, + disable_turbines=None, ): """ Shortcut method to instantiate a :py:class:`~.tools.cut_plane.CutPlane` @@ -493,6 +528,14 @@ def calculate_horizontal_plane( Defaults to None. y_bounds (tuple, optional): Limits of output array (in m). Defaults to None. + wd (float, optional): Wind direction. Defaults to None. + ws (float, optional): Wind speed. Defaults to None. + yaw_angles (NDArrayFloat, optional): Turbine yaw angles. Defaults + to None. + power_setpoints (NDArrayFloat, optional): + Turbine power setpoints. Defaults to None. + disable_turbines (NDArrayBool, optional): Boolean array on whether + to disable turbines. Defaults to None. Returns: :py:class:`~.tools.cut_plane.CutPlane`: containing values @@ -507,8 +550,6 @@ def calculate_horizontal_plane( # Store the current state for reinitialization floris_dict = self.floris.as_dict() - current_yaw_angles = self.floris.farm.yaw_angles - # Set the solver to a flow field planar grid solver_settings = { "type": "flow_field_planar_grid", @@ -517,11 +558,14 @@ def calculate_horizontal_plane( "flow_field_grid_points": [x_resolution, y_resolution], "flow_field_bounds": [x_bounds, y_bounds], } - self.reinitialize(wind_directions=wd, wind_speeds=ws, solver_settings=solver_settings) - - # TODO this has to be done here as it seems to be lost with reinitialize - if yaw_angles is not None: - self.floris.farm.yaw_angles = yaw_angles + self.set( + wind_directions=wd, + wind_speeds=ws, + solver_settings=solver_settings, + yaw_angles=yaw_angles, + power_setpoints=power_setpoints, + disable_turbines=disable_turbines, + ) # Calculate wake self.floris.solve_for_viz() @@ -546,7 +590,7 @@ def calculate_horizontal_plane( self.floris = Floris.from_dict(floris_dict) # Run the simulation again for futher postprocessing (i.e. now we can get farm power) - self.calculate_wake(yaw_angles=current_yaw_angles) + self.run() return horizontal_plane @@ -560,6 +604,8 @@ def calculate_cross_plane( wd=None, ws=None, yaw_angles=None, + power_setpoints=None, + disable_turbines=None, ): """ Shortcut method to instantiate a :py:class:`~.tools.cut_plane.CutPlane` @@ -590,7 +636,6 @@ def calculate_cross_plane( # Store the current state for reinitialization floris_dict = self.floris.as_dict() - current_yaw_angles = self.floris.farm.yaw_angles # Set the solver to a flow field planar grid solver_settings = { @@ -600,11 +645,14 @@ def calculate_cross_plane( "flow_field_grid_points": [y_resolution, z_resolution], "flow_field_bounds": [y_bounds, z_bounds], } - self.reinitialize(wind_directions=wd, wind_speeds=ws, solver_settings=solver_settings) - - # TODO this has to be done here as it seems to be lost with reinitialize - if yaw_angles is not None: - self.floris.farm.yaw_angles = yaw_angles + self.set( + wind_directions=wd, + wind_speeds=ws, + solver_settings=solver_settings, + yaw_angles=yaw_angles, + power_setpoints=power_setpoints, + disable_turbines=disable_turbines, + ) # Calculate wake self.floris.solve_for_viz() @@ -624,7 +672,7 @@ def calculate_cross_plane( self.floris = Floris.from_dict(floris_dict) # Run the simulation again for futher postprocessing (i.e. now we can get farm power) - self.calculate_wake(yaw_angles=current_yaw_angles) + self.run() return cross_plane @@ -638,6 +686,8 @@ def calculate_y_plane( wd=None, ws=None, yaw_angles=None, + power_setpoints=None, + disable_turbines=None, ): """ Shortcut method to instantiate a :py:class:`~.tools.cut_plane.CutPlane` @@ -654,6 +704,18 @@ def calculate_y_plane( Defaults to None. y_bounds (tuple, optional): Limits of output array (in m). Defaults to None. + z_bounds (tuple, optional): Limits of output array (in m). + Defaults to None. + wd (float, optional): Wind direction. Defaults to None. + ws (float, optional): Wind speed. Defaults to None. + yaw_angles (NDArrayFloat, optional): Turbine yaw angles. Defaults + to None. + power_setpoints (NDArrayFloat, optional): + Turbine power setpoints. Defaults to None. + disable_turbines (NDArrayBool, optional): Boolean array on whether + to disable turbines. Defaults to None. + + Returns: :py:class:`~.tools.cut_plane.CutPlane`: containing values @@ -668,7 +730,6 @@ def calculate_y_plane( # Store the current state for reinitialization floris_dict = self.floris.as_dict() - current_yaw_angles = self.floris.farm.yaw_angles # Set the solver to a flow field planar grid solver_settings = { @@ -678,11 +739,14 @@ def calculate_y_plane( "flow_field_grid_points": [x_resolution, z_resolution], "flow_field_bounds": [x_bounds, z_bounds], } - self.reinitialize(wind_directions=wd, wind_speeds=ws, solver_settings=solver_settings) - - # TODO this has to be done here as it seems to be lost with reinitialize - if yaw_angles is not None: - self.floris.farm.yaw_angles = yaw_angles + self.set( + wind_directions=wd, + wind_speeds=ws, + solver_settings=solver_settings, + yaw_angles=yaw_angles, + power_setpoints=power_setpoints, + disable_turbines=disable_turbines, + ) # Calculate wake self.floris.solve_for_viz() @@ -702,7 +766,7 @@ def calculate_y_plane( self.floris = Floris.from_dict(floris_dict) # Run the simulation again for futher postprocessing (i.e. now we can get farm power) - self.calculate_wake(yaw_angles=current_yaw_angles) + self.run() return y_plane @@ -730,7 +794,7 @@ def get_turbine_powers(self) -> NDArrayFloat: if self.floris.state is not State.USED: raise RuntimeError( "Can't run function `FlorisInterface.get_turbine_powers` without " - "first running `FlorisInterface.calculate_wake`." + "first running `FlorisInterface.run`." ) # Check for negative velocities, which could indicate bad model # parameters or turbines very closely spaced. @@ -872,7 +936,6 @@ def get_farm_AEP( freq, cut_in_wind_speed=0.001, cut_out_wind_speed=None, - yaw_angles=None, turbine_weights=None, no_wake=False, ) -> float: @@ -895,10 +958,6 @@ def get_farm_AEP( wind farm is known to produce 0.0 W of power. If None is specified, will assume that the wind farm does not cut out at high wind speeds. Defaults to None. - yaw_angles (NDArrayFloat | list[float] | None, optional): - The relative turbine yaw angles in degrees. If None is - specified, will assume that the turbine yaw angles are all - zero degrees for all conditions. Defaults to None. turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power @@ -952,17 +1011,14 @@ def get_farm_AEP( if np.any(conditions_to_evaluate): wind_speeds_subset = wind_speeds[conditions_to_evaluate] wind_directions_subset = wind_directions[conditions_to_evaluate] - yaw_angles_subset = None - if yaw_angles is not None: - yaw_angles_subset = yaw_angles[conditions_to_evaluate] - self.reinitialize( + self.set( wind_speeds=wind_speeds_subset, wind_directions=wind_directions_subset, ) if no_wake: - self.calculate_no_wake(yaw_angles=yaw_angles_subset) + self.run_no_wake() else: - self.calculate_wake(yaw_angles=yaw_angles_subset) + self.run() farm_power[conditions_to_evaluate] = self.get_farm_power( turbine_weights=turbine_weights ) @@ -971,7 +1027,7 @@ def get_farm_AEP( aep = np.sum(np.multiply(freq, farm_power) * 365 * 24) # Reset the FLORIS object to the full wind speed array - self.reinitialize(wind_speeds=wind_speeds, wind_directions=wind_directions) + self.set(wind_speeds=wind_speeds, wind_directions=wind_directions) return aep @@ -980,7 +1036,6 @@ def get_farm_AEP_with_wind_data( wind_data, cut_in_wind_speed=0.001, cut_out_wind_speed=None, - yaw_angles=None, turbine_weights=None, no_wake=False, ) -> float: @@ -1001,10 +1056,6 @@ def get_farm_AEP_with_wind_data( wind farm is known to produce 0.0 W of power. If None is specified, will assume that the wind farm does not cut out at high wind speeds. Defaults to None. - yaw_angles (NDArrayFloat | list[float] | None, optional): - The relative turbine yaw angles in degrees. If None is - specified, will assume that the turbine yaw angles are all - zero degrees for all conditions. Defaults to None. turbine_weights (NDArrayFloat | list[float] | None, optional): weighing terms that allow the user to emphasize power at particular turbines and/or completely ignore the power @@ -1040,7 +1091,6 @@ def get_farm_AEP_with_wind_data( freq, cut_in_wind_speed=cut_in_wind_speed, cut_out_wind_speed=cut_out_wind_speed, - yaw_angles=yaw_angles, turbine_weights=turbine_weights, no_wake=no_wake, ) @@ -1163,7 +1213,7 @@ def sample_velocity_deficit_profiles( if reference_height is None: reference_height = self.floris.flow_field.reference_wind_height - self.reinitialize( + self.set( wind_directions=[wind_direction], wind_speeds=[homogeneous_wind_speed], wind_shear=0.0, @@ -1181,7 +1231,7 @@ def sample_velocity_deficit_profiles( reference_height, ) - self.reinitialize( + self.set( wind_directions=wind_directions_copy, wind_speeds=wind_speeds_copy, wind_shear=wind_shear_copy, @@ -1226,3 +1276,17 @@ def get_turbine_layout(self, z=False): return xcoords, ycoords, zcoords else: return xcoords, ycoords + + ### v3 functions that are removed - raise an error if used + + def calculate_wake(self): + raise NotImplementedError( + "The calculate_wake method has been removed. Please use the run method. " + "See https://nrel.github.io/floris/upgrade_guides/v3_to_v4.html for more information." + ) + + def reinitialize(self): + raise NotImplementedError( + "The reinitialize method has been removed. Please use the set method. " + "See https://nrel.github.io/floris/upgrade_guides/v3_to_v4.html for more information." + ) diff --git a/floris/tools/optimization/layout_optimization/layout_optimization_base.py b/floris/tools/optimization/layout_optimization/layout_optimization_base.py index 47b8f2ccb..2396d1690 100644 --- a/floris/tools/optimization/layout_optimization/layout_optimization_base.py +++ b/floris/tools/optimization/layout_optimization/layout_optimization_base.py @@ -60,7 +60,7 @@ def _get_geoyaw_angles(self): # NOTE: requires that child class saves x and y locations # as self.x and self.y and updates them during optimization. if self.enable_geometric_yaw: - self.yaw_opt.fi_subset.reinitialize(layout_x=self.x, layout_y=self.y) + self.yaw_opt.fi_subset.set(layout_x=self.x, layout_y=self.y) df_opt = self.yaw_opt.optimize() self.yaw_angles = np.vstack(df_opt['yaw_angles_opt'])[:, :] else: diff --git a/floris/tools/optimization/layout_optimization/layout_optimization_boundary_grid.py b/floris/tools/optimization/layout_optimization/layout_optimization_boundary_grid.py index 07386b1d4..a17b3e220 100644 --- a/floris/tools/optimization/layout_optimization/layout_optimization_boundary_grid.py +++ b/floris/tools/optimization/layout_optimization/layout_optimization_boundary_grid.py @@ -612,7 +612,7 @@ def reinitialize_xy(self): self.boundary_spacing, ) - self.fi.reinitialize(layout=(layout_x, layout_y)) + self.fi.set(layout=(layout_x, layout_y)) def plot_layout(self): plt.figure(figsize=(9, 6)) diff --git a/floris/tools/optimization/layout_optimization/layout_optimization_pyoptsparse.py b/floris/tools/optimization/layout_optimization/layout_optimization_pyoptsparse.py index 75bbf9c84..555ab21cb 100644 --- a/floris/tools/optimization/layout_optimization/layout_optimization_pyoptsparse.py +++ b/floris/tools/optimization/layout_optimization/layout_optimization_pyoptsparse.py @@ -91,16 +91,15 @@ def _obj_func(self, varDict): # Parse the variable dictionary self.parse_opt_vars(varDict) - # Update turbine map with turbince locations - self.fi.reinitialize(layout_x=self.x, layout_y=self.y) - # Compute turbine yaw angles using PJ's geometric code (if enabled) yaw_angles = self._get_geoyaw_angles() + # Update turbine map with turbine locations and yaw angles + self.fi.set(layout_x=self.x, layout_y=self.y, yaw_angles=yaw_angles) # Compute the objective function funcs = {} funcs["obj"] = ( - -1 * self.fi.get_farm_AEP(self.freq, yaw_angles=yaw_angles) / self.initial_AEP + -1 * self.fi.get_farm_AEP(self.freq) / self.initial_AEP ) # Compute constraints, if any are defined for the optimization diff --git a/floris/tools/optimization/layout_optimization/layout_optimization_scipy.py b/floris/tools/optimization/layout_optimization/layout_optimization_scipy.py index e960576f4..2c66f1b67 100644 --- a/floris/tools/optimization/layout_optimization/layout_optimization_scipy.py +++ b/floris/tools/optimization/layout_optimization/layout_optimization_scipy.py @@ -99,8 +99,8 @@ def _obj_func(self, locs): self._change_coordinates(locs_unnorm) # Compute turbine yaw angles using PJ's geometric code (if enabled) yaw_angles = self._get_geoyaw_angles() - return (-1 * self.fi.get_farm_AEP(self.freq, yaw_angles=yaw_angles) / - self.initial_AEP) + self.fi.set(yaw_angles=yaw_angles) + return -1 * self.fi.get_farm_AEP(self.freq) /self.initial_AEP def _change_coordinates(self, locs): # Parse the layout coordinates @@ -112,7 +112,7 @@ def _change_coordinates(self, locs): self.y = layout_y # Update the turbine map in floris - self.fi.reinitialize(layout_x=layout_x, layout_y=layout_y) + self.fi.set(layout_x=layout_x, layout_y=layout_y) def _generate_constraints(self): tmp1 = { diff --git a/floris/tools/optimization/yaw_optimization/yaw_optimization_base.py b/floris/tools/optimization/yaw_optimization/yaw_optimization_base.py index 21643bdc5..5964c2ae1 100644 --- a/floris/tools/optimization/yaw_optimization/yaw_optimization_base.py +++ b/floris/tools/optimization/yaw_optimization/yaw_optimization_base.py @@ -349,12 +349,13 @@ def _calculate_farm_power( # Calculate solutions turbine_power = np.zeros_like(self._minimum_yaw_angle_subset[:, :]) - fi_subset.reinitialize( + fi_subset.set( wind_directions=wd_array, wind_speeds=ws_array, - turbulence_intensities=ti_array + turbulence_intensities=ti_array, + yaw_angles=yaw_angles, ) - fi_subset.calculate_wake(yaw_angles=yaw_angles) + fi_subset.run() turbine_power = fi_subset.get_turbine_powers() # Multiply with turbine weighing terms diff --git a/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py b/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py index 204a58ade..735296b58 100644 --- a/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py +++ b/floris/tools/optimization/yaw_optimization/yaw_optimizer_scipy.py @@ -73,7 +73,7 @@ def optimize(self): ti_array = self.fi_subset.floris.flow_field.turbulence_intensities for i, (wd, ws, ti) in enumerate(zip(wd_array, ws_array, ti_array)): - self.fi_subset.reinitialize( + self.fi_subset.set( wind_directions=[wd], wind_speeds=[ws], turbulence_intensities=[ti] diff --git a/floris/tools/parallel_computing_interface.py b/floris/tools/parallel_computing_interface.py index 407ab7d1c..7260b0305 100644 --- a/floris/tools/parallel_computing_interface.py +++ b/floris/tools/parallel_computing_interface.py @@ -30,7 +30,8 @@ def _load_local_floris_object( def _get_turbine_powers_serial(fi_information, yaw_angles=None): fi = _load_local_floris_object(*fi_information) - fi.calculate_wake(yaw_angles=yaw_angles) + fi.set(yaw_angles=yaw_angles) + fi.run() return (fi.get_turbine_powers(), fi.floris.flow_field) @@ -150,7 +151,7 @@ def copy(self): self_copy.fi = self.fi.copy() return self_copy - def reinitialize( + def set( self, wind_speeds=None, wind_directions=None, @@ -165,7 +166,7 @@ def reinitialize( turbine_type=None, solver_settings=None, ): - """Pass to the FlorisInterface reinitialize function. To allow users + """Pass to the FlorisInterface set function. To allow users to directly replace a FlorisInterface object with this UncertaintyInterface object, this function is required.""" @@ -178,7 +179,7 @@ def reinitialize( # Just passes arguments to the floris object fi = self.fi.copy() - fi.reinitialize( + fi.set( wind_speeds=wind_speeds, wind_directions=wind_directions, wind_shear=wind_shear, @@ -279,10 +280,11 @@ def _postprocessing(self, output): return turbine_powers - def calculate_wake(self): - # raise UserWarning("'calculate_wake' not supported. Please use - # 'get_turbine_powers' or 'get_farm_power' directly.") - return None # Do nothing + def run(self): # TODO: Remove or update this function? + raise UserWarning( + "'run' not supported on ParallelComputingInterface. Please use " + "'get_turbine_powers' or 'get_farm_power' directly." + ) def get_turbine_powers(self, yaw_angles=None): # Retrieve multiargs: preprocessing @@ -454,7 +456,7 @@ def get_farm_AEP( yaw_angles_subset = None if yaw_angles is not None: yaw_angles_subset = yaw_angles[:, conditions_to_evaluate] - self.fi.reinitialize( + self.fi.set( wind_directions=wind_direction_subset, wind_speeds=wind_speeds_subset, turbulence_intensities=turbulence_intensities_subset, @@ -467,7 +469,7 @@ def get_farm_AEP( aep = np.sum(np.multiply(freq, farm_power) * 365 * 24) # Reset the FLORIS object to the full wind speed array - self.fi.reinitialize( + self.fi.set( wind_directions=wind_directions, wind_speeds=wind_speeds, turbulence_intensities=turbulence_intensities_subset, diff --git a/floris/tools/visualization.py b/floris/tools/visualization.py index 59a2a7465..eb54650ae 100644 --- a/floris/tools/visualization.py +++ b/floris/tools/visualization.py @@ -16,6 +16,7 @@ from scipy.spatial import ConvexHull from floris.simulation import Floris +from floris.simulation.turbine.operation_models import POWER_SETPOINT_DEFAULT from floris.tools.cut_plane import CutPlane from floris.tools.floris_interface import FlorisInterface from floris.type_dec import ( @@ -590,6 +591,8 @@ def calculate_horizontal_plane_with_turbines( wd=None, ws=None, yaw_angles=None, + power_setpoints=None, + disable_turbines=None, ) -> CutPlane: """ This function creates a :py:class:`~.tools.cut_plane.CutPlane` by @@ -614,6 +617,8 @@ def calculate_horizontal_plane_with_turbines( wd (float, optional): Wind direction setting. Defaults to None. ws (float, optional): Wind speed setting. Defaults to None. yaw_angles (np.ndarray, optional): Yaw angles settings. Defaults to None. + power_setpoints (np.ndarray, optional): Power setpoints settings. Defaults to None. + disable_turbines (np.ndarray, optional): Disable turbines settings. Defaults to None. Returns: :py:class:`~.tools.cut_plane.CutPlane`: containing values of x, y, u, v, w @@ -630,15 +635,15 @@ def calculate_horizontal_plane_with_turbines( fi.check_wind_condition_for_viz(wd=wd, ws=ws) # Set the ws and wd - fi.reinitialize(wind_directions=wd, wind_speeds=ws) - - # Re-set yaw angles - if yaw_angles is not None: - fi.floris.farm.yaw_angles = yaw_angles - - # Now place the yaw_angles back into yaw_angles - # to be sure not None + fi.set( + wind_directions=wd, + wind_speeds=ws, + yaw_angles=yaw_angles, + power_setpoints=power_setpoints, + disable_turbines=disable_turbines + ) yaw_angles = fi.floris.farm.yaw_angles + power_setpoints = fi.floris.farm.power_setpoints # Grab the turbine layout layout_x = copy.deepcopy(fi.layout_x) @@ -650,14 +655,18 @@ def calculate_horizontal_plane_with_turbines( layout_x_test = np.append(layout_x,[0]) layout_y_test = np.append(layout_y,[0]) - # Declare turbine types with an extra turbine in - # case of special one type useage + # Declare turbine types with an extra turbine in case of special one-type usage if len(layout_x) > 1 and len(turbine_types) == 1: # Convert to list length len(layout_x) + 1 turbine_types_test = [turbine_types[0] for i in range(len(layout_x))] + ['nrel_5MW'] else: turbine_types_test = np.append(turbine_types, 'nrel_5MW').tolist() yaw_angles = np.append(yaw_angles, np.zeros([fi.floris.flow_field.n_findex, 1]), axis=1) + power_setpoints = np.append( + power_setpoints, + POWER_SETPOINT_DEFAULT * np.ones([fi.floris.flow_field.n_findex, 1]), + axis=1 + ) # Get a grid of points test test if x_bounds is None: @@ -689,12 +698,15 @@ def calculate_horizontal_plane_with_turbines( # Place the test turbine at this location and calculate wake layout_x_test[-1] = x layout_y_test[-1] = y - fi.reinitialize( + fi.set( layout_x=layout_x_test, layout_y=layout_y_test, + yaw_angles=yaw_angles, + power_setpoints=power_setpoints, + disable_turbines=disable_turbines, turbine_type=turbine_types_test ) - fi.calculate_wake(yaw_angles=yaw_angles) + fi.run() # Get the velocity of that test turbines central point center_point = int(np.floor(fi.floris.flow_field.u[0,-1].shape[0] / 2.0)) diff --git a/tests/floris_interface_integration_test.py b/tests/floris_interface_integration_test.py index 694322c7f..93243950f 100644 --- a/tests/floris_interface_integration_test.py +++ b/tests/floris_interface_integration_test.py @@ -16,53 +16,127 @@ def test_read_yaml(): fi = FlorisInterface(configuration=YAML_INPUT) assert isinstance(fi, FlorisInterface) -def test_calculate_wake(): +def test_set_run(): """ - In FLORIS v3.2, running calculate_wake twice incorrectly set the yaw angles when the first time - has non-zero yaw settings but the second run had all-zero yaw settings. The test below asserts - that the yaw angles are correctly set in subsequent calls to calculate_wake. + These tests are designed to test the set / run sequence to ensure that inputs are + set when they should be, not set when they shouldn't be, and that the run sequence + retains or resets information as intended. """ + + # In FLORIS v3.2, running calculate_wake twice incorrectly set the yaw angles when the + # first time has non-zero yaw settings but the second run had all-zero yaw settings. + # The test below asserts that the yaw angles are correctly set in subsequent calls to run. fi = FlorisInterface(configuration=YAML_INPUT) yaw_angles = 20 * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) - fi.calculate_wake(yaw_angles=yaw_angles) + fi.set(yaw_angles=yaw_angles) + fi.run() assert fi.floris.farm.yaw_angles == yaw_angles yaw_angles = np.zeros((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) - fi.calculate_wake(yaw_angles=yaw_angles) + fi.set(yaw_angles=yaw_angles) + fi.run() 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 + # Verify making changes to the layout, wind speed, and wind direction both before and after + # running the calculation + fi.reset_operation() + fi.set(layout_x=[0, 0], layout_y=[0, 1000], wind_speeds=[8, 8], wind_directions=[270, 270]) + assert np.array_equal(fi.floris.farm.layout_x, np.array([0, 0])) + assert np.array_equal(fi.floris.farm.layout_y, np.array([0, 1000])) + assert np.array_equal(fi.floris.flow_field.wind_speeds, np.array([8, 8])) + assert np.array_equal(fi.floris.flow_field.wind_directions, np.array([270, 270])) + + # Double check that nothing has changed after running the calculation + fi.run() + assert np.array_equal(fi.floris.farm.layout_x, np.array([0, 0])) + assert np.array_equal(fi.floris.farm.layout_y, np.array([0, 1000])) + assert np.array_equal(fi.floris.flow_field.wind_speeds, np.array([8, 8])) + assert np.array_equal(fi.floris.flow_field.wind_directions, np.array([270, 270])) + + # Verify that changing wind shear doesn't change the other settings above + fi.set(wind_shear=0.1) + assert fi.floris.flow_field.wind_shear == 0.1 + assert np.array_equal(fi.floris.farm.layout_x, np.array([0, 0])) + assert np.array_equal(fi.floris.farm.layout_y, np.array([0, 1000])) + assert np.array_equal(fi.floris.flow_field.wind_speeds, np.array([8, 8])) + assert np.array_equal(fi.floris.flow_field.wind_directions, np.array([270, 270])) + + # Verify that operation set-points are retained after changing other settings + yaw_angles = 20 * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) + fi.set(yaw_angles=yaw_angles) + assert np.array_equal(fi.floris.farm.yaw_angles, yaw_angles) + fi.set() + assert np.array_equal(fi.floris.farm.yaw_angles, yaw_angles) + fi.set(wind_speeds=[10, 10]) + assert np.array_equal(fi.floris.farm.yaw_angles, yaw_angles) + power_setpoints = 1e6 * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) + fi.set(power_setpoints=power_setpoints) + assert np.array_equal(fi.floris.farm.yaw_angles, yaw_angles) + assert np.array_equal(fi.floris.farm.power_setpoints, power_setpoints) + + # Test that setting power setpoints through the .set() function actually sets the + # power setpoints in the floris object + fi.reset_operation() + power_setpoints = 1e6 * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) + fi.set(power_setpoints=power_setpoints) + fi.run() + assert np.array_equal(fi.floris.farm.power_setpoints, power_setpoints) + + # Similar to above, any "None" set-points should be set to the default value + power_setpoints = np.array([[1e6, None]]) + fi.set(layout_x=[0, 0], layout_y=[0, 1000], power_setpoints=power_setpoints) + fi.run() + assert np.array_equal( + fi.floris.farm.power_setpoints, + np.array([[power_setpoints[0, 0], POWER_SETPOINT_DEFAULT]]) + ) - fi.calculate_wake(power_setpoints=None) +def test_reset_operation(): + # Calling the reset function should reset the power setpoints to the default values + fi = FlorisInterface(configuration=YAML_INPUT) + yaw_angles = 20 * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) + power_setpoints = 1e6 * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) + fi.set(power_setpoints=power_setpoints, yaw_angles=yaw_angles) + fi.run() + fi.reset_operation() + assert fi.floris.farm.yaw_angles == np.zeros( + (fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines) + ) 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]]) + # Double check that running the calculate also doesn't change the operating set points + fi.run() + assert fi.floris.farm.yaw_angles == np.zeros( + (fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines) + ) + assert fi.floris.farm.power_setpoints == ( + POWER_SETPOINT_DEFAULT * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) ) -def test_calculate_no_wake(): - """ - In FLORIS v3.2, running calculate_no_wake twice incorrectly set the yaw angles when the first - time has non-zero yaw settings but the second run had all-zero yaw settings. The test below - asserts that the yaw angles are correctly set in subsequent calls to calculate_no_wake. - """ +def test_run_no_wake(): + # In FLORIS v3.2, running calculate_no_wake twice incorrectly set the yaw angles when the first + # time has non-zero yaw settings but the second run had all-zero yaw settings. The test below + # asserts that the yaw angles are correctly set in subsequent calls to run_no_wake. fi = FlorisInterface(configuration=YAML_INPUT) yaw_angles = 20 * np.ones((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) - fi.calculate_no_wake(yaw_angles=yaw_angles) + fi.set(yaw_angles=yaw_angles) + fi.run_no_wake() assert fi.floris.farm.yaw_angles == yaw_angles yaw_angles = np.zeros((fi.floris.flow_field.n_findex, fi.floris.farm.n_turbines)) - fi.calculate_no_wake(yaw_angles=yaw_angles) + fi.set(yaw_angles=yaw_angles) + fi.run_no_wake() assert fi.floris.farm.yaw_angles == yaw_angles + # With no wake and three turbines in a line, the power for all turbines with zero yaw + # should be the same + fi.reset_operation() + fi.set(layout_x=[0, 200, 4000], layout_y=[0, 0, 0]) + fi.run_no_wake() + power_no_wake = fi.get_turbine_powers() + assert len(np.unique(power_no_wake)) == 1 def test_get_turbine_powers(): # Get turbine powers should return n_findex x n_turbine powers @@ -78,14 +152,14 @@ def test_get_turbine_powers(): layout_y = np.array([0, 1000]) n_turbines = len(layout_x) - fi.reinitialize( + fi.set( wind_speeds=wind_speeds, wind_directions=wind_directions, layout_x=layout_x, layout_y=layout_y, ) - fi.calculate_wake() + fi.run() turbine_powers = fi.get_turbine_powers() @@ -93,7 +167,6 @@ def test_get_turbine_powers(): assert turbine_powers.shape[1] == n_turbines assert turbine_powers[0, 0] == turbine_powers[1, 0] - def test_get_farm_power(): fi = FlorisInterface(configuration=YAML_INPUT) @@ -105,14 +178,14 @@ def test_get_farm_power(): layout_y = np.array([0, 1000]) # n_turbines = len(layout_x) - fi.reinitialize( + fi.set( wind_speeds=wind_speeds, wind_directions=wind_directions, layout_x=layout_x, layout_y=layout_y, ) - fi.calculate_wake() + fi.run() turbine_powers = fi.get_turbine_powers() farm_powers = fi.get_farm_power() @@ -155,45 +228,51 @@ def test_disable_turbines(): ) as t: turbine_type = yaml.safe_load(t) turbine_type["power_thrust_model"] = "mixed" - fi.reinitialize(turbine_type=[turbine_type]) + fi.set(turbine_type=[turbine_type]) # Init to n-findex = 2, n_turbines = 3 - fi.reinitialize( + fi.set( wind_speeds=np.array([8.,8.,]), wind_directions=np.array([270.,270.]), layout_x = [0,1000,2000], layout_y=[0,0,0] ) - # Confirm that passing in a disable value with wrong n_findex raises error + # Confirm that using a disable value with wrong n_findex raises error with pytest.raises(ValueError): - fi.calculate_wake(disable_turbines=np.zeros((10, 3), dtype=bool)) + fi.set(disable_turbines=np.zeros((10, 3), dtype=bool)) + fi.run() - # Confirm that passing in a disable value with wrong n_turbines raises error + # Confirm that using a disable value with wrong n_turbines raises error with pytest.raises(ValueError): - fi.calculate_wake(disable_turbines=np.zeros((2, 10), dtype=bool)) + fi.set(disable_turbines=np.zeros((2, 10), dtype=bool)) + fi.run() # Confirm that if all turbines are disabled, power is near 0 for all turbines - fi.calculate_wake(disable_turbines=np.ones((2, 3), dtype=bool)) + fi.set(disable_turbines=np.ones((2, 3), dtype=bool)) + fi.run() turbines_powers = fi.get_turbine_powers() - np.testing.assert_allclose(turbines_powers,0,atol=0.1) + np.testing.assert_allclose(turbines_powers, 0, atol=0.1) - # Confirm the same for calculate_no_wake - fi.calculate_no_wake(disable_turbines=np.ones((2, 3), dtype=bool)) + # Confirm the same for run_no_wake + fi.run_no_wake() turbines_powers = fi.get_turbine_powers() - np.testing.assert_allclose(turbines_powers,0,atol=0.1) + np.testing.assert_allclose(turbines_powers, 0, atol=0.1) # Confirm that if all disabled values set to false, equivalent to running normally - fi.calculate_wake() + fi.reset_operation() + fi.run() turbines_powers_normal = fi.get_turbine_powers() - fi.calculate_wake(disable_turbines=np.zeros((2, 3), dtype=bool)) + fi.set(disable_turbines=np.zeros((2, 3), dtype=bool)) + fi.run() turbines_powers_false_disable = fi.get_turbine_powers() np.testing.assert_allclose(turbines_powers_normal,turbines_powers_false_disable,atol=0.1) - # Confirm the same for calculate_no_wake - fi.calculate_no_wake() + # Confirm the same for run_no_wake + fi.run_no_wake() turbines_powers_normal = fi.get_turbine_powers() - fi.calculate_no_wake(disable_turbines=np.zeros((2, 3), dtype=bool)) + fi.set(disable_turbines=np.zeros((2, 3), dtype=bool)) + fi.run_no_wake() turbines_powers_false_disable = fi.get_turbine_powers() np.testing.assert_allclose(turbines_powers_normal,turbines_powers_false_disable,atol=0.1) @@ -201,19 +280,27 @@ def test_disable_turbines(): # In terms of impact on third turbine disable_turbines = np.zeros((2, 3), dtype=bool) disable_turbines[:,1] = [True, True] - fi.calculate_wake(disable_turbines=disable_turbines) + fi.set(disable_turbines=disable_turbines) + fi.run() power_with_middle_disabled = fi.get_turbine_powers() - fi.reinitialize(layout_x = [0,2000],layout_y = [0, 0]) - fi.calculate_wake() - power_with_middle_removed = fi.get_turbine_powers() + # Two turbine case to compare against above + fi_remove_middle = fi.copy() + fi_remove_middle.set(layout_x=[0,2000], layout_y=[0, 0]) + fi_remove_middle.run() + power_with_middle_removed = fi_remove_middle.get_turbine_powers() np.testing.assert_almost_equal(power_with_middle_disabled[0,2], power_with_middle_removed[0,1]) np.testing.assert_almost_equal(power_with_middle_disabled[1,2], power_with_middle_removed[1,1]) # Check that yaw angles are correctly set when turbines are disabled - fi.reinitialize(layout_x = [0,1000,2000],layout_y = [0,0,0]) - fi.calculate_wake(disable_turbines=disable_turbines, yaw_angles=np.ones((2, 3))) + fi.set( + layout_x=[0, 1000, 2000], + layout_y=[0, 0, 0], + disable_turbines=disable_turbines, + yaw_angles=np.ones((2, 3)) + ) + fi.run() assert (fi.floris.farm.yaw_angles == np.array([[1.0, 0.0, 1.0], [1.0, 0.0, 1.0]])).all() def test_get_farm_aep(): @@ -227,14 +314,14 @@ def test_get_farm_aep(): layout_y = np.array([0, 1000]) # n_turbines = len(layout_x) - fi.reinitialize( + fi.set( wind_speeds=wind_speeds, wind_directions=wind_directions, layout_x=layout_x, layout_y=layout_y, ) - fi.calculate_wake() + fi.run() farm_powers = fi.get_farm_power() @@ -249,7 +336,6 @@ def test_get_farm_aep(): # In this case farm_aep should match farm powers np.testing.assert_allclose(farm_aep, aep) - def test_get_farm_aep_with_conditions(): fi = FlorisInterface(configuration=YAML_INPUT) @@ -261,14 +347,14 @@ def test_get_farm_aep_with_conditions(): layout_y = np.array([0, 1000]) # n_turbines = len(layout_x) - fi.reinitialize( + fi.set( wind_speeds=wind_speeds, wind_directions=wind_directions, layout_x=layout_x, layout_y=layout_y, ) - fi.calculate_wake() + fi.run() farm_powers = fi.get_farm_power() @@ -292,23 +378,20 @@ def test_get_farm_aep_with_conditions(): #Confirm n_findex reset after the operation assert n_findex == fi.floris.flow_field.n_findex - -def test_reinitailize_ti(): +def test_set_ti(): fi = FlorisInterface(configuration=YAML_INPUT) - # Set wind directions and wind speeds and turbulence intensitities - # with n_findex = 3 - fi.reinitialize( + # Set wind directions, wind speeds and turbulence intensities with n_findex = 3 + fi.set( wind_speeds=[8.0, 8.0, 8.0], wind_directions=[240.0, 250.0, 260.0], turbulence_intensities=[0.1, 0.1, 0.1], ) # Now confirm can change wind speeds and directions shape without changing - # turbulence intensity since this is allowed when the turbulence - # intensities are uniform + # turbulence intensity since this is allowed when the turbulence intensities are uniform # raises n_findex to 4 - fi.reinitialize( + fi.set( wind_speeds=[8.0, 8.0, 8.0, 8.0], wind_directions=[ 240.0, @@ -322,16 +405,16 @@ def test_reinitailize_ti(): np.testing.assert_allclose(fi.floris.flow_field.turbulence_intensities, [0.1, 0.1, 0.1, 0.1]) # Now should be able to change turbulence intensity to changing, so long as length 4 - fi.reinitialize(turbulence_intensities=[0.08, 0.09, 0.1, 0.11]) + fi.set(turbulence_intensities=[0.08, 0.09, 0.1, 0.11]) # However the wrong length should raise an error with pytest.raises(ValueError): - fi.reinitialize(turbulence_intensities=[0.08, 0.09, 0.1]) + fi.set(turbulence_intensities=[0.08, 0.09, 0.1]) # Also, now that TI is not a single unique value, it can not be left default when changing # shape of wind speeds and directions with pytest.raises(ValueError): - fi.reinitialize( + fi.set( wind_speeds=[8.0, 8.0, 8.0, 8.0, 8.0], wind_directions=[ 240.0, @@ -343,8 +426,8 @@ def test_reinitailize_ti(): ) # Test that applying a 1D array of length 1 is allowed for ti - fi.reinitialize(turbulence_intensities=[0.12]) + fi.set(turbulence_intensities=[0.12]) # Test that applying a float however raises an error with pytest.raises(TypeError): - fi.reinitialize(turbulence_intensities=0.12) + fi.set(turbulence_intensities=0.12) diff --git a/tests/parallel_computing_interface_integration_test.py b/tests/parallel_computing_interface_integration_test.py index f55fe631c..6b31297d5 100644 --- a/tests/parallel_computing_interface_integration_test.py +++ b/tests/parallel_computing_interface_integration_test.py @@ -27,7 +27,7 @@ def test_parallel_turbine_powers(sample_inputs_fixture): fi_serial = FlorisInterface(sample_inputs_fixture.floris) fi_parallel_input = copy.deepcopy(fi_serial) - fi_serial.calculate_wake() + fi_serial.run() serial_turbine_powers = fi_serial.get_turbine_powers() diff --git a/tests/reg_tests/yaw_optimization_regression_test.py b/tests/reg_tests/yaw_optimization_regression_test.py index c9e79ff23..049aee508 100644 --- a/tests/reg_tests/yaw_optimization_regression_test.py +++ b/tests/reg_tests/yaw_optimization_regression_test.py @@ -84,7 +84,7 @@ def test_serial_refine(sample_inputs_fixture): wd_array = np.arange(0.0, 360.0, 90.0) ws_array = 8.0 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW - fi.reinitialize( + fi.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, @@ -114,20 +114,21 @@ def test_geometric_yaw(sample_inputs_fixture): wd_array = np.arange(0.0, 360.0, 90.0) ws_array = 8.0 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW - fi.reinitialize( + fi.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array, wind_speeds=ws_array, ) - fi.calculate_wake() + fi.run() baseline_farm_power = fi.get_farm_power().squeeze() yaw_opt = YawOptimizationGeometric(fi) df_opt = yaw_opt.optimize() yaw_angles_opt_geo = np.vstack(yaw_opt.yaw_angles_opt) - fi.calculate_wake(yaw_angles=yaw_angles_opt_geo) + fi.set(yaw_angles=yaw_angles_opt_geo) + fi.run() geo_farm_power = fi.get_farm_power().squeeze() df_opt['farm_power_baseline'] = baseline_farm_power @@ -161,7 +162,7 @@ def test_scipy_yaw_opt(sample_inputs_fixture): wd_array = np.arange(0.0, 360.0, 90.0) ws_array = 8.0 * np.ones_like(wd_array) D = 126.0 # Rotor diameter for the NREL 5 MW - fi.reinitialize( + fi.set( layout_x=[0.0, 5 * D, 10 * D], layout_y=[0.0, 0.0, 0.0], wind_directions=wd_array,