@@ -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