@@ -168,8 +168,8 @@ def apply_static_rate_limits(
168
168
ws_step = ws_array [1 ] - ws_array [0 ]
169
169
ti_step = ti_array [1 ] - ti_array [0 ] if len (ti_array ) > 1 else 1
170
170
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)
173
173
offsets_array = offsets_all .reshape (
174
174
(len (wd_array ), len (ws_array ), len (ti_array ), offsets_all .shape [- 1 ])
175
175
)
@@ -255,6 +255,7 @@ def compute_hysteresis_zones(
255
255
"""
256
256
257
257
# Extract yaw offsets, wind directions
258
+ check_df_opt_ordering (df_opt )
258
259
offsets_stacked = np .vstack (df_opt .yaw_angles_opt .to_numpy ())
259
260
wind_directions = np .unique (df_opt .wind_direction )
260
261
offsets = offsets_stacked .reshape (
@@ -349,6 +350,7 @@ def apply_wind_speed_ramps(
349
350
pd.DataFrame: A yaw offset lookup table for all wind speeds between ws_min and ws_max
350
351
with wind speed ramps applied.
351
352
"""
353
+ check_df_opt_ordering (df_opt )
352
354
353
355
# Check valid ordering of wind speeds
354
356
if (ws_wake_steering_cut_in
@@ -455,6 +457,7 @@ def get_yaw_angles_interpolant(df_opt):
455
457
dimensions, and returns the yaw angles for all turbines. This function
456
458
incorporates the ramp-up and ramp-down regions.
457
459
"""
460
+ check_df_opt_ordering (df_opt )
458
461
459
462
# Extract points and values
460
463
wind_directions = np .unique (df_opt ["wind_direction" ])
@@ -465,8 +468,6 @@ def get_yaw_angles_interpolant(df_opt):
465
468
# Store for possible use if no turbulence intensity is provided
466
469
ti_ref = float (np .median (turbulence_intensities ))
467
470
468
- # TODO: check ordering in df_opt is correct
469
-
470
471
# Reshape the yaw offsets to match the wind direction, wind speed, and turbulence intensity
471
472
yaw_offsets = yaw_offsets .reshape (
472
473
len (wind_directions ),
@@ -565,4 +566,40 @@ def create_uniform_wind_rose(
565
566
wind_speeds = wind_speeds ,
566
567
wind_directions = wind_directions ,
567
568
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