From 2b27aea078b9bbd4cf385ea5102c5563c00eb330 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Fri, 28 Nov 2025 11:29:39 +0000 Subject: [PATCH 1/5] add write_pp_df_parquets to WindUpConfig --- pyproject.toml | 4 ++-- uv.lock | 2 +- wind_up/main_analysis.py | 6 ++++++ wind_up/models.py | 4 ++++ 4 files changed, 13 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 53dc692..53ef910 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "res-wind-up" -version = "0.4.5" +version = "0.4.6" authors = [ { name = "Alex Clerc", email = "alex.clerc@res-group.com" } ] @@ -97,7 +97,7 @@ max-complexity = 12 # try to bring this down to 10 [tool.ruff.lint.pylint] max-branches = 14 # try to bring this down to 12 -max-statements = 66 # try to bring this down to 50 +max-statements = 67 # try to bring this down to 50 max-args = 17 # try to bring this down to 5 [tool.ruff.lint.per-file-ignores] diff --git a/uv.lock b/uv.lock index 3c56752..3b0ab3e 100644 --- a/uv.lock +++ b/uv.lock @@ -3596,7 +3596,7 @@ wheels = [ [[package]] name = "res-wind-up" -version = "0.4.5" +version = "0.4.6" source = { editable = "." } dependencies = [ { name = "eval-type-backport" }, diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index 07c6cec..becf018 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -726,6 +726,12 @@ def _calc_test_ref_results( random_seed=random_seed, ) + if cfg.write_pp_df_parquets: + (cfg.out_dir / "pp_df").mkdir(exist_ok=True) + pre_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_pre_df.parquet") + post_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_post_df.parquet") + _pp_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_pp_df.parquet") + other_results = ref_info | { "ref_ws_col": ref_ws_col, "distance_m": distance_m, diff --git a/wind_up/models.py b/wind_up/models.py index b408170..d5024c4 100644 --- a/wind_up/models.py +++ b/wind_up/models.py @@ -174,6 +174,10 @@ class WindUpConfig(BaseModel): default=10 * 60, description="Timebase in seconds for SCADA data, other data is converted to this timebase", ) + write_pp_df_parquets: bool = Field( + default=False, + description="If true the power performance parquet files are written along with other results and plots.", + ) ignore_turbine_anemometer_data: bool = Field( default=False, description="If true do not use turbine anemometer data for anything", From da69f3751c352c0723d1456c1b5a276dca3de4c7 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Fri, 28 Nov 2025 12:21:04 +0000 Subject: [PATCH 2/5] write out detrend_df as well --- wind_up/main_analysis.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index becf018..dd5bc9e 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -728,6 +728,7 @@ def _calc_test_ref_results( if cfg.write_pp_df_parquets: (cfg.out_dir / "pp_df").mkdir(exist_ok=True) + detrend_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_detrend_df.parquet") pre_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_pre_df.parquet") post_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_post_df.parquet") _pp_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_pp_df.parquet") From 2e2375a926abf0b2a5b16642ec96ba527ce39e0a Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Fri, 28 Nov 2025 14:32:50 +0000 Subject: [PATCH 3/5] remove write detrend_df --- wind_up/main_analysis.py | 1 - 1 file changed, 1 deletion(-) diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index dd5bc9e..becf018 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -728,7 +728,6 @@ def _calc_test_ref_results( if cfg.write_pp_df_parquets: (cfg.out_dir / "pp_df").mkdir(exist_ok=True) - detrend_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_detrend_df.parquet") pre_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_pre_df.parquet") post_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_post_df.parquet") _pp_df.to_parquet(cfg.out_dir / "pp_df" / f"{test_wtg.name}_{ref_name}_pp_df.parquet") From 13230f949eb9b7b9d85c52ee2039a96f74fb3bd8 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Tue, 20 Jan 2026 17:57:18 +0000 Subject: [PATCH 4/5] improve input_data_timeline --- wind_up/plots/input_data.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/wind_up/plots/input_data.py b/wind_up/plots/input_data.py index 72b7c98..c790fb3 100644 --- a/wind_up/plots/input_data.py +++ b/wind_up/plots/input_data.py @@ -84,7 +84,7 @@ def plot_input_data_timeline( show_plots: bool = True, scada_data_column_for_power: str = DataColumns.active_power_mean, scada_data_column_for_yaw_angle: str = DataColumns.yaw_angle_mean, -) -> plt.Figure: +) -> None: """Plot timeline of input data with key milestones and data exclusions. This function does not do any data filtering itself, but instead only displays the data as it is provided. @@ -281,6 +281,10 @@ def plot_input_data_timeline( box = a.get_position() a.set_position([box.x0, box.y0, box.width * 0.8, box.height]) # type: ignore[arg-type] handles, labels = a.get_legend_handles_labels() + # remove duplicates from handles and labels + by_label = dict(zip(labels, handles,strict=True)) + handles = list(by_label.values()) + labels = list(by_label.keys()) a.legend(handles[::-1], labels[::-1], bbox_to_anchor=(1.04, 1), borderaxespad=0) if save_to_folder is not None: @@ -288,7 +292,4 @@ def plot_input_data_timeline( save_to_folder.mkdir(parents=True, exist_ok=True) fig.savefig(save_to_folder / "input_data_timeline_fig.png") - if not show_plots: - plt.close(fig) - - return fig + plt.close(fig) From bd5cda48d0792f1e5769f5c5191efba0e39b8fc1 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Wed, 21 Jan 2026 08:57:55 +0000 Subject: [PATCH 5/5] fix lint issues --- pyproject.toml | 2 +- wind_up/main_analysis.py | 1 - wind_up/plots/input_data.py | 10 ++++------ 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 53ef910..1193669 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -97,7 +97,7 @@ max-complexity = 12 # try to bring this down to 10 [tool.ruff.lint.pylint] max-branches = 14 # try to bring this down to 12 -max-statements = 67 # try to bring this down to 50 +max-statements = 68 # try to bring this down to 50 max-args = 17 # try to bring this down to 5 [tool.ruff.lint.per-file-ignores] diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index becf018..dd2a219 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -805,7 +805,6 @@ def run_wind_up_analysis( plot_input_data_timeline( assessment_inputs=inputs, save_to_folder=inputs.plot_cfg.plots_dir if inputs.plot_cfg.save_plots else None, - show_plots=inputs.plot_cfg.show_plots, ) wf_df = inputs.wf_df diff --git a/wind_up/plots/input_data.py b/wind_up/plots/input_data.py index c790fb3..9df69ed 100644 --- a/wind_up/plots/input_data.py +++ b/wind_up/plots/input_data.py @@ -81,7 +81,6 @@ def plot_input_data_timeline( figsize: tuple[int, int] | None = None, height_ratios: tuple[int, int] | None = None, save_to_folder: Path | None = None, - show_plots: bool = True, scada_data_column_for_power: str = DataColumns.active_power_mean, scada_data_column_for_yaw_angle: str = DataColumns.yaw_angle_mean, ) -> None: @@ -93,7 +92,6 @@ def plot_input_data_timeline( :param figsize: size of the plot figure, if `None` it will be auto-sized based on the number of turbines :param height_ratios: ratios for the two subplots, if `None` it will be auto-sized based on the number of turbines :param save_to_folder: directory in which to save the plot - :param show_plots: whether to show the interactive plot or not :param scada_data_column_for_power: column name in the wind farm DataFrame to use for power data coverage plotting :param scada_data_column_for_yaw_angle: column name in the wind farm DataFrame to use for yaw data coverage plotting :return: figure object @@ -281,10 +279,10 @@ def plot_input_data_timeline( box = a.get_position() a.set_position([box.x0, box.y0, box.width * 0.8, box.height]) # type: ignore[arg-type] handles, labels = a.get_legend_handles_labels() - # remove duplicates from handles and labels - by_label = dict(zip(labels, handles,strict=True)) - handles = list(by_label.values()) - labels = list(by_label.keys()) + # remove duplicate labels + handles_by_label = dict(zip(labels, handles, strict=True)) # type:ignore[call-overload] + handles = list(handles_by_label.values()) + labels = list(handles_by_label.keys()) a.legend(handles[::-1], labels[::-1], bbox_to_anchor=(1.04, 1), borderaxespad=0) if save_to_folder is not None: