From 8c4b62528b3c47336fc295df13443d24eaffef25 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 partially complete --- floris/tools/floris_interface.py | 35 +++++++++++++++++ tests/floris_interface_test.py | 65 +++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 2 deletions(-) diff --git a/floris/tools/floris_interface.py b/floris/tools/floris_interface.py index 437e27e14..764169e04 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,40 @@ 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.farm.construct_coordinates() + + self.floris.flow_field.wind_speeds = wind_speeds + self.floris.flow_field.wind_directions = wind_directions + + self.grid = TurbineGrid( + turbine_coordinates=self.floris.farm.coordinates, + reference_turbine_diameter=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..7f656d3df 100644 --- a/tests/floris_interface_test.py +++ b/tests/floris_interface_test.py @@ -72,5 +72,66 @@ def test_calculate_no_wake(): assert 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) + + + # NOTE: fi.floris.flow_field.wind_directions returns a numpy array of shape (n wind directions) + # If you wrap that in a list below, then it's a list of numpy arrays that is cast to a numpy + # array by the FlowField attrs. The FlowField.wind_directions is used to create the grid, + # so now the Grid data structures have strange shapes. + # The solution is to ensure that these are not unnecessarily wrapped in lists, but this + # will be time consuming to enforce. + # This isn't a problem here because the Grid is not being reinitialized so the shape of the + # data structures is not yet changed. + # fi.reset( + # wind_directions=[fi.floris.flow_field.wind_directions], + # wind_speeds=[fi.floris.flow_field.wind_speeds], + # ) + # print(fi.floris.flow_field.wind_directions) + + + # 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 + 10, + wind_speeds=fi.floris.flow_field.wind_speeds + 10, + ) + fi.calculate_wake() + rotated_turbine_powers = fi.get_turbine_powers() + assert np.array_equal(baseline_turbine_powers, rotated_turbine_powers) + + print(baseline_turbine_powers) + print(rotated_turbine_powers) +