Skip to content

Commit ccfe8a8

Browse files
committed
Ensure that the used df_opt is in the correct ordering of wind direction, wind speed, and turbulence intensity.
1 parent 461d60f commit ccfe8a8

File tree

2 files changed

+60
-5
lines changed

2 files changed

+60
-5
lines changed

tests/wake_steering_design_test.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
apply_wind_speed_ramps,
99
build_simple_wake_steering_lookup_table,
1010
build_uncertain_wake_steering_lookup_table,
11+
check_df_opt_ordering,
1112
compute_hysteresis_zones,
1213
create_uniform_wind_rose,
1314
get_yaw_angles_interpolant,
@@ -281,3 +282,20 @@ def test_create_uniform_wind_rose():
281282
wind_rose = create_uniform_wind_rose()
282283
frequencies = wind_rose.unpack_freq()
283284
assert (frequencies == frequencies[0]).all()
285+
286+
def test_check_df_opt_ordering():
287+
288+
# Pass tests
289+
df_opt = generic_df_opt()
290+
check_df_opt_ordering(df_opt)
291+
292+
# Remove a row so that not all data is present
293+
with pytest.raises(ValueError):
294+
check_df_opt_ordering(df_opt.drop(0))
295+
296+
# Artificially create bad ordering by swapping columns
297+
df_opt_2 = df_opt.copy()
298+
df_opt_2.wind_speed = df_opt_2.wind_direction
299+
df_opt_2.wind_direction = df_opt.wind_speed
300+
with pytest.raises(ValueError):
301+
check_df_opt_ordering(df_opt_2)

whoc/design_tools/wake_steering_design.py

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,8 @@ def apply_static_rate_limits(
168168
ws_step = ws_array[1] - ws_array[0]
169169
ti_step = ti_array[1] - ti_array[0] if len(ti_array) > 1 else 1
170170

171-
# 4D array, with dimensions: (turbine, wd, ws, ti)
172-
# TODO: will this ordering always work? Or not?
171+
check_df_opt_ordering(df_opt)
172+
# 4D array, with dimensions: (wd, ws, ti, turbines)
173173
offsets_array = offsets_all.reshape(
174174
(len(wd_array), len(ws_array), len(ti_array), offsets_all.shape[-1])
175175
)
@@ -255,6 +255,7 @@ def compute_hysteresis_zones(
255255
"""
256256

257257
# Extract yaw offsets, wind directions
258+
check_df_opt_ordering(df_opt)
258259
offsets_stacked = np.vstack(df_opt.yaw_angles_opt.to_numpy())
259260
wind_directions = np.unique(df_opt.wind_direction)
260261
offsets = offsets_stacked.reshape(
@@ -349,6 +350,7 @@ def apply_wind_speed_ramps(
349350
pd.DataFrame: A yaw offset lookup table for all wind speeds between ws_min and ws_max
350351
with wind speed ramps applied.
351352
"""
353+
check_df_opt_ordering(df_opt)
352354

353355
# Check valid ordering of wind speeds
354356
if (ws_wake_steering_cut_in
@@ -455,6 +457,7 @@ def get_yaw_angles_interpolant(df_opt):
455457
dimensions, and returns the yaw angles for all turbines. This function
456458
incorporates the ramp-up and ramp-down regions.
457459
"""
460+
check_df_opt_ordering(df_opt)
458461

459462
# Extract points and values
460463
wind_directions = np.unique(df_opt["wind_direction"])
@@ -465,8 +468,6 @@ def get_yaw_angles_interpolant(df_opt):
465468
# Store for possible use if no turbulence intensity is provided
466469
ti_ref = float(np.median(turbulence_intensities))
467470

468-
# TODO: check ordering in df_opt is correct
469-
470471
# Reshape the yaw offsets to match the wind direction, wind speed, and turbulence intensity
471472
yaw_offsets = yaw_offsets.reshape(
472473
len(wind_directions),
@@ -565,4 +566,40 @@ def create_uniform_wind_rose(
565566
wind_speeds=wind_speeds,
566567
wind_directions=wind_directions,
567568
ti_table=ti,
568-
)
569+
)
570+
571+
def check_df_opt_ordering(df_opt):
572+
"""
573+
Check that the ordering of inputs is first wind direction, then wind speed,
574+
then turbulence intensity.
575+
576+
Raises an error if this is found not to be the case.
577+
578+
Args:
579+
df_opt (pd.DataFrame): A yaw offset lookup table.
580+
"""
581+
582+
inputs_all = df_opt[["wind_direction", "wind_speed", "turbulence_intensity"]].to_numpy()
583+
wd_unique = np.unique(df_opt["wind_direction"])
584+
ws_unique = np.unique(df_opt["wind_speed"])
585+
ti_unique = np.unique(df_opt["turbulence_intensity"])
586+
587+
# Check full
588+
if not inputs_all.shape[0] == len(wd_unique)*len(ws_unique)*len(ti_unique):
589+
raise ValueError(
590+
"All combinations of wind direction, wind speed, and turbulence intensity "
591+
"must be specified."
592+
)
593+
594+
# Check order is correct
595+
wds_reshaped = inputs_all[:,0].reshape((len(wd_unique), len(ws_unique), len(ti_unique)))
596+
wss_reshaped = inputs_all[:,1].reshape((len(wd_unique), len(ws_unique), len(ti_unique)))
597+
tis_reshaped = inputs_all[:,2].reshape((len(wd_unique), len(ws_unique), len(ti_unique)))
598+
599+
if (not np.all(wds_reshaped == wd_unique[:,None,None])
600+
or not np.all(wss_reshaped == ws_unique[None,:,None])
601+
or not np.all(tis_reshaped == ti_unique[None,None,:])):
602+
raise ValueError(
603+
"df_opt must be ordered first by wind direction, then by wind speed,"
604+
"then by turbulence intensity."
605+
)

0 commit comments

Comments
 (0)