From 50bcc6e9360562c7db13ea2cf92e9a0fbac4af0d Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Wed, 19 Nov 2025 13:52:40 +0000 Subject: [PATCH 1/3] support lidar data via AssessmentInputs --- wind_up/interface.py | 5 ++++- wind_up/main_analysis.py | 10 ++++++---- wind_up/models.py | 4 ---- wind_up/reanalysis_data.py | 8 ++++++++ 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/wind_up/interface.py b/wind_up/interface.py index 577c99e..ba189ab 100644 --- a/wind_up/interface.py +++ b/wind_up/interface.py @@ -13,7 +13,7 @@ from wind_up.constants import REANALYSIS_WD_COL from wind_up.northing import add_wf_yawdir, apply_northing_corrections from wind_up.optimize_northing import auto_northing_corrections -from wind_up.reanalysis_data import ReanalysisDataset, add_reanalysis_data +from wind_up.reanalysis_data import MastOrLiDARDataset, ReanalysisDataset, add_reanalysis_data from wind_up.scada_funcs import filter_scada_df, get_raw_scada_and_cfg_from_file from wind_up.scada_power_curve import calc_pc_and_rated_ws from wind_up.smart_data import add_smart_lat_long_to_cfg @@ -143,6 +143,7 @@ class AssessmentInputs: cfg: WindUpConfig plot_cfg: PlotConfig pre_post_splitter: PrePostSplitter + mast_or_lidar_datasets: list[MastOrLiDARDataset] | None = None @classmethod def from_cfg( @@ -155,6 +156,7 @@ def from_cfg( toggle_df: pd.DataFrame | None = None, reanalysis_datasets: list[ReanalysisDataset], cache_dir: Path | None = None, + mast_or_lidar_datasets: list[MastOrLiDARDataset] | None = None, ) -> AssessmentInputs: """Construct instance of AssessmentInputs from configuration objects and data. @@ -183,6 +185,7 @@ def from_cfg( cfg=cfg, plot_cfg=plot_cfg, pre_post_splitter=pre_post_splitter, + mast_or_lidar_datasets=mast_or_lidar_datasets, ) diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index 5e27dac..683977c 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -12,7 +12,6 @@ import wind_up from wind_up.circular_math import circ_diff from wind_up.constants import ( - PROJECTROOT_DIR, RANDOM_SEED, REANALYSIS_WD_COL, REANALYSIS_WS_COL, @@ -33,6 +32,7 @@ ) from wind_up.plots.yaw_direction_plots import plot_yaw_direction_pre_post from wind_up.pp_analysis import pre_post_pp_analysis_with_reversal_and_bootstrapping +from wind_up.reanalysis_data import MastOrLiDARDataset from wind_up.result_manager import result_manager from wind_up.waking_state import ( add_waking_scen, @@ -183,6 +183,7 @@ def _get_ref_df( test_wtg: Turbine, toggle_df: pd.DataFrame | None = None, keep_only_toggle_off: bool = True, + mast_or_lidar_datasets: list[MastOrLiDARDataset] | None = None, ) -> pd.DataFrame: if ref_name in [x.name for x in cfg.asset.wtgs]: ref_df = wf_df.loc[ref_name].copy() @@ -201,9 +202,7 @@ def _get_ref_df( original_wd_col = REANALYSIS_WD_COL elif ref_name in [x.name for x in cfg.asset.masts_and_lidars]: ref_obj = next(x for x in cfg.asset.masts_and_lidars if x.name == ref_name) - ref_df = pd.read_parquet( - PROJECTROOT_DIR / "input_data" / "masts_and_lidars" / cfg.asset.name / f"{ref_obj.data_file_name}", - ) + ref_df = next(x for x in mast_or_lidar_datasets if x.id == ref_name).data northing_df = wf_df.loc[cfg.test_wtgs[0].name, [REANALYSIS_WS_COL, REANALYSIS_WD_COL, WINDFARM_YAWDIR_COL]] ref_df = ref_df.merge(northing_df, how="left", left_index=True, right_index=True) original_ws_col = ref_obj.wind_speed_column @@ -449,6 +448,7 @@ def _calc_test_ref_results( plot_cfg: PlotConfig, random_seed: int, toggle_df: pd.DataFrame | None = None, + mast_or_lidar_datasets: list[MastOrLiDARDataset] | None = None, ) -> dict: test_name = test_wtg.name (plot_cfg.plots_dir / test_name / ref_name).mkdir(exist_ok=True, parents=True) @@ -478,6 +478,7 @@ def _calc_test_ref_results( test_wtg=test_wtg, toggle_df=toggle_df, keep_only_toggle_off=keep_only_toggle_off, + mast_or_lidar_datasets=mast_or_lidar_datasets, ) if len(ref_df) == 0: result_manager.warning(f"ref_df is empty for {ref_name}") @@ -925,6 +926,7 @@ def run_wind_up_analysis( cfg=cfg, plot_cfg=plot_cfg, random_seed=random_seed, + mast_or_lidar_datasets=inputs.mast_or_lidar_datasets, ) test_ref_results = test_ref_results | test_results logger.info(test_ref_results) diff --git a/wind_up/models.py b/wind_up/models.py index da39bbb..b408170 100644 --- a/wind_up/models.py +++ b/wind_up/models.py @@ -97,10 +97,6 @@ class MastOrLidar(BaseModel): name: str = Field(description="Object name", min_length=2) latitude: float = Field(default=np.nan, ge=-90, le=90) longitude: float = Field(default=np.nan, ge=-180, le=180) - data_file_name: str = Field( - min_length=2, - description="Name of data timeseries file", - ) wind_speed_column: str = Field(min_length=2, description="Name of wind speed column in data timeseries file") wind_direction_column: str = Field( min_length=2, diff --git a/wind_up/reanalysis_data.py b/wind_up/reanalysis_data.py index c7e599c..456153c 100644 --- a/wind_up/reanalysis_data.py +++ b/wind_up/reanalysis_data.py @@ -222,3 +222,11 @@ def add_reanalysis_data( if plot_cfg is not None: plot_wf_and_reanalysis_sample_timeseries(wf_df=wf_and_reanalysis_df, plot_cfg=plot_cfg) return wf_and_reanalysis_df + + +@dataclass +class MastOrLiDARDataset: + """Class to store mast or LiDAR data.""" + + id: str + data: pd.DataFrame From dd8032bc89100bbfa081eb259c710f9e6d3b9326 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Wed, 19 Nov 2025 13:53:08 +0000 Subject: [PATCH 2/3] Update pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index c4450f4..53dc692 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "res-wind-up" -version = "0.4.4" +version = "0.4.5" authors = [ { name = "Alex Clerc", email = "alex.clerc@res-group.com" } ] From a934898452c44a171dfd789c925d8b3768d7e3d9 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Wed, 19 Nov 2025 14:23:41 +0000 Subject: [PATCH 3/3] fix lint issues --- uv.lock | 2 +- wind_up/main_analysis.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/uv.lock b/uv.lock index 546ea39..3c56752 100644 --- a/uv.lock +++ b/uv.lock @@ -3596,7 +3596,7 @@ wheels = [ [[package]] name = "res-wind-up" -version = "0.4.4" +version = "0.4.5" source = { editable = "." } dependencies = [ { name = "eval-type-backport" }, diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index 683977c..07c6cec 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -32,7 +32,6 @@ ) from wind_up.plots.yaw_direction_plots import plot_yaw_direction_pre_post from wind_up.pp_analysis import pre_post_pp_analysis_with_reversal_and_bootstrapping -from wind_up.reanalysis_data import MastOrLiDARDataset from wind_up.result_manager import result_manager from wind_up.waking_state import ( add_waking_scen, @@ -44,6 +43,7 @@ if TYPE_CHECKING: from wind_up.models import PlotConfig, Turbine, WindUpConfig + from wind_up.reanalysis_data import MastOrLiDARDataset logger = logging.getLogger(__name__) @@ -202,6 +202,9 @@ def _get_ref_df( original_wd_col = REANALYSIS_WD_COL elif ref_name in [x.name for x in cfg.asset.masts_and_lidars]: ref_obj = next(x for x in cfg.asset.masts_and_lidars if x.name == ref_name) + if mast_or_lidar_datasets is None: + msg = f"No mast or lidar datasets provided for {ref_name}" + raise ValueError(msg) ref_df = next(x for x in mast_or_lidar_datasets if x.id == ref_name).data northing_df = wf_df.loc[cfg.test_wtgs[0].name, [REANALYSIS_WS_COL, REANALYSIS_WD_COL, WINDFARM_YAWDIR_COL]] ref_df = ref_df.merge(northing_df, how="left", left_index=True, right_index=True)