[go: up one dir, main page]

Skip to content

Commit

Permalink
[BUGFIX] Circular upsampling across wind directions (#943)
Browse files Browse the repository at this point in the history
* Initial upsample fixes

* Upsample testing changes

* clean up code

* clean up tests

* matching changes to wind_ti_rose

* update wind_ti_rose tests

* Address comments

* Fix setup of random opt test

* Clean up comment

* Fix examples

* Add utility function to identify step size

* Add test of step size function

* Add a function to make wind direction adjacent

* Add test in wind direction adjacent function

* Improve docstring

* Return sorting indices

* Update to use new utilities

* Update to test new methods

* Switch to concatenate from double append.

* hide labels for boundary, as not visible under heterogeneous map.

---------

Co-authored-by: misi9170 <michael.sinner@nrel.gov>
  • Loading branch information
paulf81 and misi9170 authored Jul 15, 2024
1 parent 3d13d91 commit f3ac07f
Show file tree
Hide file tree
Showing 7 changed files with 863 additions and 262 deletions.
2 changes: 1 addition & 1 deletion examples/003_wind_data_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@
# bins for which frequency is zero are not simulated. This can be changed by setting the
# compute_zero_freq_occurrence parameter to True.
wind_directions = np.array([200.0, 300.0])
wind_speeds = np.array([5.0, 1.00])
wind_speeds = np.array([5.0, 10.0])
freq_table = np.array([[0.5, 0], [0.5, 0]])
wind_rose = WindRose(
wind_directions=wind_directions, wind_speeds=wind_speeds, ti_table=0.06, freq_table=freq_table
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@

# Setup 2 wind directions (due east and due west)
# and 1 wind speed with uniform probability
wind_directions = np.array([270.0, 90.0])
wind_directions = np.array([90.0, 270.0])
n_wds = len(wind_directions)
wind_speeds = [8.0] * np.ones_like(wind_directions)
turbulence_intensities = 0.06 * np.ones_like(wind_directions)
wind_speeds = np.array([8.0])

# Shape frequency distribution to match number of wind directions and wind speeds
freq_table = np.ones((len(wind_directions), len(wind_speeds)))
freq_table = freq_table / freq_table.sum()
Expand Down Expand Up @@ -106,7 +106,7 @@
fig = plt.gcf()
sm = ax.tricontourf(x_locs, y_locs, speed_multipliers[0], cmap="coolwarm")
fig.colorbar(sm, ax=ax, label="Speed multiplier")
ax.legend(["Initial layout", "Optimized layout", "Optimization boundary"])
ax.legend(["_Optimization boundary", "Initial layout", "Optimized layout" ])
ax.set_title("Geometric yaw disabled")


Expand Down Expand Up @@ -144,7 +144,7 @@
fig = plt.gcf()
sm = ax.tricontourf(x_locs, y_locs, speed_multipliers[0], cmap="coolwarm")
fig.colorbar(sm, ax=ax, label="Speed multiplier")
ax.legend(["Initial layout", "Optimized layout", "Optimization boundary"])
ax.legend(["_Optimization boundary", "Initial layout", "Optimized layout"])
ax.set_title("Geometric yaw enabled")

print(
Expand Down
101 changes: 101 additions & 0 deletions floris/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,107 @@ def wrap_360(x):

return x % 360.0

def check_and_identify_step_size(wind_directions):
"""
This function identifies the step size in a series of wind directions. The function will
return the step size if the wind directions are evenly spaced, otherwise it will raise an
error.
Args:
wind_directions (np.ndarray): Array of wind directions.
Returns:
float: The step size of the wind directions.
"""

if len(wind_directions) < 2:
raise ValueError("Array must contain at least 2 elements")

# First compute the steps between each wind direction
steps = np.diff(wind_directions)

# Confirm that the steps are all positive
if not np.all(steps > 0):
raise ValueError("wind_directions must be monotonically increasing")

# Check the step from the last to the first element
last_step = wind_directions[0] - wind_directions[-1] + 360

# If len(window_directions) == 2, then return whichever step is smaller
if len(wind_directions) == 2:
return min(steps[0], last_step)

# If len(window_directions) == 3 make some checks
elif len(wind_directions) == 3:
if np.all(steps == steps[0]):
return steps[0]
elif steps[0] == last_step:
return steps[0]
elif steps[1] == last_step:
return steps[1]
else:
raise ValueError("wind_directions must be evenly spaced")

else:
if np.all(steps == steps[0]):
return steps[0]

# If all but one of the steps are the same
values, counts = np.unique(steps, return_counts=True)

# Check for the case where there are more than two different step sizes
if len(values) > 2:
raise ValueError("wind_directions must be evenly spaced")

# In the case there are only two step sizes, ensure that one only happens once
if np.min(counts) > 1:
raise ValueError("wind_directions must be evenly spaced")

# If the last step equals the most common step, return the most common step
if last_step == values[np.argmax(counts)]:
return values[np.argmax(counts)]

raise ValueError("wind_directions must be evenly spaced")

def make_wind_directions_adjacent(wind_directions: NDArrayFloat) -> NDArrayFloat:
"""
This function reorders the wind directions so that they are adjacent. The function will
return the reordered wind directions if the wind directions are not adjacent, otherwise it
will return the input wind directions
Args:
wind_directions (NDArrayFloat): Array of wind directions.
Returns:
NDArrayFloat: The reordered wind directions to be adjacent.
"""

# Check the step size of the wind directions
step_size = check_and_identify_step_size(wind_directions)

# Get a list of steps
steps = np.diff(wind_directions)

# There will be at most one step with a size larger than the step size
# If there is one, find it
if np.any(steps > step_size):
idx = np.argmax(steps)

# Now change wind_directions such that for each direction after that index
# subtract 360 and move that block to the front
wind_directions = np.concatenate(
(wind_directions[idx+1:] - 360, wind_directions[:idx+1])
)

# Return the wind directions and indices to go from the original to the new
sort_indices = np.array(list(range(idx+1,len(wind_directions))) + list(range(idx+1)))

return wind_directions, sort_indices

else:

return wind_directions, np.arange(len(wind_directions))


def wind_delta(wind_directions: NDArrayFloat | float):
"""
Expand Down
Loading

0 comments on commit f3ac07f

Please sign in to comment.