NREL / floris

A controls-oriented engineering wake model.
http://nrel.github.io/floris
BSD 3-Clause "New" or "Revised" License
200 stars 153 forks source link

Bug report: heterogeneous inflow is interpolated on wind-aligned coordinate system #579

Closed Bartdoekemeijer closed 1 year ago

Bartdoekemeijer commented 1 year ago

Bug description The heterogeneous map functionality uses the wrong (wind-aligned) coordinate system, rather than the north-aligned/inertial frame coordinate system.

Example 16 demonstrates the het_map heterogeneous inflow functionality. The user can specify an input according to: https://github.com/NREL/floris/blob/8436fd78b002e5792f5d0dd1409332d171036d49/examples/16_heterogeneous_inflow.py#L42

speed_ups = [[2.0, 2.0, 1.0, 1.0]]
x_locs = [-300.0, -300.0, 2600.0, 2600.0]
y_locs = [ -300.0, 300.0, -300.0, 300.0]

Which suggests that x_locs and y_locs and are in the same coordinate system as fi.layout_x and fi_layout_y, what I call the inertial coordinate system. This coordinate system is independent of the ambient wind direction.

Then, a linear interpolant is created between these x and y coordinates and the speed-ups in floris_interface.py, something like https://github.com/NREL/floris/blob/8436fd78b002e5792f5d0dd1409332d171036d49/floris/tools/floris_interface.py#L905

Now, when calculating the initial flow field in flow_field.py, the speed-ups are calculated using grid.x_sorted and grid.y_sorted, as follows: https://github.com/NREL/floris/blob/8436fd78b002e5792f5d0dd1409332d171036d49/floris/simulation/flow_field.py#L94

This is fundamentally incorrect. grid.x_sorted and grid.y_sorted are in the wind-aligned frame and thus have a different value depending on the wind direction. The grid coordinates inserted into the interpolant should correspond to the inertial frame, not the wind-aligned frame.

To Reproduce

  1. Checkout on the branch in PR #578
  2. Run the following example:
from floris.tools import FlorisInterface
from floris.tools.floris_interface import generate_heterogeneous_wind_map

speed_ups = [[2.0, 2.0, 1.0, 1.0], [2.0, 2.0, 1.0, 1.0]]
x_locs = [-300.0, -300.0, 2600.0, 2600.0]
y_locs = [ -300.0, 300.0, -300.0, 300.0]

# Generate the linear interpolation to be used for the heterogeneous inflow.
het_map_2d = generate_heterogeneous_wind_map(speed_ups, x_locs, y_locs)

# Initialize FLORIS with a heterogeneous inflow
fi_2d = FlorisInterface("inputs/gch.yaml", het_map=het_map_2d)
fi_2d.reinitialize(
    wind_shear=0.0,
    wind_directions=[255.0, 270.0],
    wind_speeds=[8.0],
)
fi_2d.calculate_wake()

print("grid.x_sorted for first turbine, wind direction of 255.0 deg")
print(fi_2d.floris.grid.x_sorted[0, 0, 0, :, :])
print("grid.x_sorted for first turbine, wind direction of 270.0 deg")
print(fi_2d.floris.grid.x_sorted[1, 0, 0, :, :])

print("grid.x_sorted_inertial_frame for first turbine, wind direction of 255.0 deg")
print(fi_2d.floris.grid.x_sorted_inertial_frame[0, 0, 0, :, :])
print("grid.x_sorted_inertial_frame for first turbine, wind direction of 270.0 deg")
print(fi_2d.floris.grid.x_sorted_inertial_frame[1, 0, 0, :, :])

results in

grid.x_sorted for first turbine, wind direction of 255.0 deg
[[21.46672944 21.46672944 21.46672944]
 [21.46672944 21.46672944 21.46672944]
 [21.46672944 21.46672944 21.46672944]]
grid.x_sorted for first turbine, wind direction of 270.0 deg
[[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]
grid.x_sorted_inertial_frame for first turbine, wind direction of 255.0 deg
[[ 8.15279992e+00  8.15279992e+00  8.15279992e+00]
 [-2.27373675e-13 -2.27373675e-13 -2.27373675e-13]
 [-8.15279992e+00 -8.15279992e+00 -8.15279992e+00]]
grid.x_sorted_inertial_frame for first turbine, wind direction of 270.0 deg
[[0. 0. 0.]
 [0. 0. 0.]
 [0. 0. 0.]]

Showing that the correct usage here should be inserting grid.x_sorted_inertial_frame and grid.y_sorted_inertial_frame into the heterogeneous map linear interpolant.

Expected behavior The heterogeneous map functionality linearly interpolates the speed-ups based on an inertial/north-aligned coordinate system.

Screenshots, if applicable

**Floris Version** `commit 59f3a6` **System Information (please complete the following information):** - OS: Ubuntu 22.04 LTS - Library versions * matplotlib==3.6.3 * numpy==1.24.2 * pytest==7.1.3 * scipy==1.10.0 **Additional information** While looking at the `het_map` functionality, I struggled with its interface. It requires a list of interpolants, one per wind direction, and assumes the speed-ups are independent of wind speed. Have you thought about a single interpolant for the entire floris object that takes in `(wind_direction_grid, wind_speed_grid, grid_x, grid_y, grid_z)`? If the user then doesn't want speed-up dependency on the ambient wind speed, we can just duplicate values behind the curtains to match these dimensions. It also means users do not need to fully reinitialize the interpolants when changing the wind directions in the floris object.
misi9170 commented 1 year ago

@Bartdoekemeijer @bayc Closing as this was addressed in #578 . Please reopen if necessary.