From 96c6e10c26752bc36673e76bcb08f7e9849c42f5 Mon Sep 17 00:00:00 2001 From: Rafael M Mudafort Date: Thu, 7 Dec 2023 10:49:06 -0600 Subject: [PATCH] Add reset function to FlorisInterface --- floris/tools/floris_interface.py | 34 +++++++++++++++ tests/floris_interface_test.py | 72 +++++++++++++++++++++++++++++--- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index a466ad583..324ccd286 100644 --- a/floris/tools/floris_interface.py +++ b/floris/tools/floris_interface.py @@ -32,6 +32,7 @@ from floris.simulation.turbine_multi_dim import multidim_power_down_select, power_multidim from floris.tools.cut_plane import CutPlane from floris.type_dec import NDArrayFloat +from floris.simulation.grid import TurbineGrid class FlorisInterface(LoggingManager): @@ -188,6 +189,39 @@ def calculate_no_wake( # Finalize values to user-supplied order self.floris.finalize() + def reset( + self, + layout_x: NDArrayFloat, + layout_y: NDArrayFloat, + wind_speeds: NDArrayFloat, + wind_directions: NDArrayFloat + ): + # NOTE: if the layout changes, then the grid will need to be reinitialized. The grid + # points are placed in space based on the farm.layout. + # The size of the grid arrays can remain the same, but the grid points will need to be + # reset. + + self.floris.farm.layout_x = layout_x + self.floris.farm.layout_y = layout_y + # TODO: the hub heights are expanded to 3d in the farm, so this takes the first wind + # condition as the reference hub height array. It should instead either not change + # the shape of the hub heights array or get the raw data differently. + self.floris.farm.hub_heights = self.floris.farm.hub_heights[0,0] + + self.floris.flow_field.wind_speeds = wind_speeds + self.floris.flow_field.wind_directions = wind_directions + + self.floris.grid = TurbineGrid( + turbine_coordinates=self.floris.farm.coordinates, + turbine_diameters=self.floris.farm.rotor_diameters[0,0], + wind_directions=self.floris.flow_field.wind_directions, + wind_speeds=self.floris.flow_field.wind_speeds, + grid_resolution=self.floris.solver["turbine_grid_points"], + time_series=self.floris.flow_field.time_series, + ) + self.floris.initialize_domain() + + # @profile def reinitialize( self, wind_speeds: list[float] | NDArrayFloat | None = None, diff --git a/tests/floris_interface_test.py b/tests/floris_interface_test.py index 494576983..fac246f52 100644 --- a/tests/floris_interface_test.py +++ b/tests/floris_interface_test.py @@ -31,7 +31,7 @@ def test_calculate_wake(): ) ) fi.calculate_wake(yaw_angles=yaw_angles) - assert fi.floris.farm.yaw_angles == yaw_angles + assert np.array_equal(fi.floris.farm.yaw_angles, yaw_angles) yaw_angles = np.zeros( ( @@ -41,7 +41,7 @@ def test_calculate_wake(): ) ) fi.calculate_wake(yaw_angles=yaw_angles) - assert fi.floris.farm.yaw_angles == yaw_angles + assert np.array_equal(fi.floris.farm.yaw_angles, yaw_angles) def test_calculate_no_wake(): @@ -59,7 +59,7 @@ def test_calculate_no_wake(): ) ) fi.calculate_no_wake(yaw_angles=yaw_angles) - assert fi.floris.farm.yaw_angles == yaw_angles + assert np.array_equal(fi.floris.farm.yaw_angles, yaw_angles) yaw_angles = np.zeros( ( @@ -69,8 +69,68 @@ def test_calculate_no_wake(): ) ) fi.calculate_no_wake(yaw_angles=yaw_angles) - assert fi.floris.farm.yaw_angles == yaw_angles + assert np.array_equal(fi.floris.farm.yaw_angles, yaw_angles) -def test_reinitialize(): - pass +def test_reset(): + """ + Test that the reset function correctly resets the given arguments, and that + it correctly resets the low level FLORIS objects. + + When we change the value of the wind speed, FlowField should reinitialize the velocity + arrays with the updated shear profile. + When we change the value of the wind direction, Grid should sort the points, as needed, + and reset the sorted_indeces that are used in Farm. Farm should also reset all of it's + sorted and unsorted data arrays accordingly. + + When we change the size of either of these, all data arrays should be resized based on + the number of wind speeds and wind directions. + """ + + # First, test that after resetting the wind speed and direction and layout, the calculation + # results haven't changed. + fi = FlorisInterface(configuration=YAML_INPUT) + fi.calculate_wake() + baseline_turbine_powers = fi.get_turbine_powers() + + fi.reset( + layout_x=fi.floris.farm.layout_x, + layout_y=fi.floris.farm.layout_y, + wind_directions=fi.floris.flow_field.wind_directions, + wind_speeds=fi.floris.flow_field.wind_speeds, + ) + fi.calculate_wake() + test_turbine_powers = fi.get_turbine_powers() + + assert np.array_equal(baseline_turbine_powers, test_turbine_powers) + + # Since the test layout is three turbines in a line, rotating 180 degrees should produce + # the same farm power and flipped turbine powers + fi.reset( + layout_x=fi.floris.farm.layout_x, + layout_y=fi.floris.farm.layout_y, + wind_directions=fi.floris.flow_field.wind_directions + 180, + wind_speeds=fi.floris.flow_field.wind_speeds, + ) + fi.calculate_wake() + rotated_turbine_powers = fi.get_turbine_powers() + assert np.array_equal( + baseline_turbine_powers, + rotated_turbine_powers[:,:,::-1] # turbine dimension is flipped below + ) + + # At 90 degree rotation, all turbines are unwaked so all turbine powers should be equal + # to the upstream turbine in the baseline array + fi.reset( + layout_x=fi.floris.farm.layout_x, + layout_y=fi.floris.farm.layout_y, + wind_directions=fi.floris.flow_field.wind_directions + 90, + wind_speeds=fi.floris.flow_field.wind_speeds, + ) + fi.calculate_wake() + unwaked_turbine_powers = fi.get_turbine_powers() + n_turbines = fi.floris.farm.n_turbines + assert np.array_equal( + np.repeat(baseline_turbine_powers[:,:,0:1], n_turbines, axis=2), + unwaked_turbine_powers + )