From 2b27aea078b9bbd4cf385ea5102c5563c00eb330 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Fri, 28 Nov 2025 11:29:39 +0000 Subject: [PATCH 01/16] 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 02/16] 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 03/16] 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 04/16] 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 05/16] 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: From 94ed45c3558bad92aa2368974403ae7f616cba92 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Wed, 22 Apr 2026 18:36:48 +0100 Subject: [PATCH 06/16] fix plot_input_data_timeline test --- wind_up/main_analysis.py | 5 ++++- wind_up/plots/input_data.py | 8 ++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index 89904fe..c103788 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -8,6 +8,7 @@ import numpy as np import pandas as pd +from matplotlib import pyplot as plt import wind_up from wind_up.circular_math import circ_diff @@ -803,10 +804,12 @@ def run_wind_up_analysis( preprocess_warning_counts = len(result_manager.stored_warnings) result_manager.stored_warnings = [] - plot_input_data_timeline( + _fig = 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, ) + plt.close(_fig) wf_df = inputs.wf_df pc_per_ttype = inputs.pc_per_ttype diff --git a/wind_up/plots/input_data.py b/wind_up/plots/input_data.py index 8927e59..68a20d3 100644 --- a/wind_up/plots/input_data.py +++ b/wind_up/plots/input_data.py @@ -104,9 +104,10 @@ def plot_input_data_timeline( # noqa: PLR0915 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: +) -> plt.Figure: """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. @@ -334,4 +335,7 @@ def plot_input_data_timeline( # noqa: PLR0915 save_to_folder.mkdir(parents=True, exist_ok=True) fig.savefig(save_to_folder / "input_data_timeline_fig.png") - plt.close(fig) + if not show_plots: + plt.close(fig) + + return fig From 0f1aee8bfc498425415155121cc7a59c95c25530 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Wed, 22 Apr 2026 18:37:33 +0100 Subject: [PATCH 07/16] restore docstring --- wind_up/plots/input_data.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wind_up/plots/input_data.py b/wind_up/plots/input_data.py index 68a20d3..a5fccfa 100644 --- a/wind_up/plots/input_data.py +++ b/wind_up/plots/input_data.py @@ -116,6 +116,7 @@ def plot_input_data_timeline( # noqa: PLR0915 :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 From 717480ed92890ccf576ed23dc94dd4e217732224 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Fri, 8 May 2026 16:01:03 +0100 Subject: [PATCH 08/16] add missing mkdir --- wind_up/caching.py | 1 + 1 file changed, 1 insertion(+) diff --git a/wind_up/caching.py b/wind_up/caching.py index e4cdfd8..c25da48 100644 --- a/wind_up/caching.py +++ b/wind_up/caching.py @@ -31,6 +31,7 @@ def wrap(func: Callable[..., Any]) -> Callable[..., Any]: def wrapped_f(*a: Any, **kw: Any) -> Any: # noqa fresh_cache = False if not Path(fp).is_file() or not use_cache or Path(fp).stat().st_size == 0: + fp.parent.mkdir(parents=True, exist_ok=True) with Path.open(fp, "wb") as f: pickle.dump(func(*a, **kw), f) fresh_cache = True From d41a2f8eebfe7c9597839b67534971eda2b9119f Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Fri, 8 May 2026 16:03:03 +0100 Subject: [PATCH 09/16] change OUTPUT_DIR --- wind_up/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/wind_up/constants.py b/wind_up/constants.py index 82a8767..7fcf0cf 100644 --- a/wind_up/constants.py +++ b/wind_up/constants.py @@ -7,7 +7,7 @@ TURBINE_DATA_DIR = Path(__file__).parents[1] / "input_data/turbine_data" REANALYSIS_DIR = Path(__file__).parents[1] / "input_data/reanalysis" TOGGLE_DIR = Path(__file__).parents[1] / "input_data/toggle" -OUTPUT_DIR = Path(__file__).parents[1] / "output" +OUTPUT_DIR = Path.home() / "wind-up-output" RANDOM_SEED = 0 SCATTER_S = 1 From c11d4da3b98d395c3e9f8845309596b95ca4e343 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Fri, 8 May 2026 16:03:27 +0100 Subject: [PATCH 10/16] improve yaw_direction plot --- wind_up/plots/yaw_direction_plots.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wind_up/plots/yaw_direction_plots.py b/wind_up/plots/yaw_direction_plots.py index 6a0bac5..7c18553 100644 --- a/wind_up/plots/yaw_direction_plots.py +++ b/wind_up/plots/yaw_direction_plots.py @@ -142,11 +142,12 @@ def _get_mid(x: float | pd.Interval) -> float: index="ws_bin_centre", columns="wd_bin_centre", values=signal_name, observed=False ).iloc[::-1], annot=True, - cmap="YlGnBu", + cmap="RdBu_r", fmt=".1f", linewidths=0.5, - vmin=0, + vmin=-20, vmax=20, + center=0, cbar_kws={"label": f"{signal_name.replace('_', ' ')} [deg]"}, ) From edf11408eb84723e998dba3b947529d7e4418c63 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Fri, 8 May 2026 16:04:59 +0100 Subject: [PATCH 11/16] bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d5b7fa4..9cd8d1a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "res-wind-up" -version = "0.4.8" +version = "0.4.9" authors = [ { name = "Alex Clerc", email = "alex.clerc@res-group.com" } ] From c1d2f626a4dc89725aa29dfec1ce92bcc2997b3e Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Mon, 11 May 2026 18:55:58 +0100 Subject: [PATCH 12/16] add close to plot_toggle_active_vs_reactive_power --- wind_up/plots/scada_funcs_plots.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/wind_up/plots/scada_funcs_plots.py b/wind_up/plots/scada_funcs_plots.py index 4a5cbe3..ca8a624 100644 --- a/wind_up/plots/scada_funcs_plots.py +++ b/wind_up/plots/scada_funcs_plots.py @@ -359,6 +359,8 @@ def plot_toggle_active_vs_reactive_power( t_dir = plot_cfg.plots_dir / wtg_name if sub_dir is None else plot_cfg.plots_dir / sub_dir t_dir.mkdir(exist_ok=True, parents=True) fig.savefig(t_dir / f"{plot_title}.png") + if not plot_cfg.show_plots: + plt.close(fig) return None From 00f1018b7d0a761774c9a2861153d5f55a772299 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Tue, 12 May 2026 07:46:44 +0100 Subject: [PATCH 13/16] try another approach --- wind_up/main_analysis.py | 6 ++++-- wind_up/plots/scada_funcs_plots.py | 14 +++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index c103788..141ca75 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -585,7 +585,7 @@ def _calc_test_ref_results( pre_df = pre_df.merge(ref_df, how="left", left_index=True, right_index=True) post_df = post_df.merge(ref_df, how="left", left_index=True, right_index=True) - compare_active_and_reactive_power_pre_post( + _fig = compare_active_and_reactive_power_pre_post( pre_df=pre_df, post_df=post_df, wtg_name=ref_name, @@ -595,6 +595,7 @@ def _calc_test_ref_results( sub_dir=f"{test_name}/{ref_name}", is_toggle_test=cfg.toggle is not None, ) + plt.close(_fig) ref_ops_curve_shift_dict = _check_for_ops_curve_shift( pre_df, @@ -881,7 +882,7 @@ def run_wind_up_analysis( test_df, pre_df, post_df = pre_post_splitter.split(test_df, test_wtg_name=test_name) - compare_active_and_reactive_power_pre_post( + _fig = compare_active_and_reactive_power_pre_post( pre_df=pre_df, post_df=post_df, wtg_name=test_name, @@ -890,6 +891,7 @@ def run_wind_up_analysis( plot_cfg=plot_cfg, is_toggle_test=cfg.toggle is not None, ) + plt.close(_fig) test_ops_curve_shift_dict = _check_for_ops_curve_shift( pre_df, diff --git a/wind_up/plots/scada_funcs_plots.py b/wind_up/plots/scada_funcs_plots.py index ca8a624..2233415 100644 --- a/wind_up/plots/scada_funcs_plots.py +++ b/wind_up/plots/scada_funcs_plots.py @@ -359,9 +359,7 @@ def plot_toggle_active_vs_reactive_power( t_dir = plot_cfg.plots_dir / wtg_name if sub_dir is None else plot_cfg.plots_dir / sub_dir t_dir.mkdir(exist_ok=True, parents=True) fig.savefig(t_dir / f"{plot_title}.png") - if not plot_cfg.show_plots: - plt.close(fig) - return None + return fig def compare_active_and_reactive_power_pre_post( @@ -374,7 +372,7 @@ def compare_active_and_reactive_power_pre_post( plot_cfg: PlotConfig, is_toggle_test: bool, sub_dir: str | None = None, -) -> None: +) -> plt.Figure | None: """Plot active vs reactive power Distinguishes data pre- and post-upgrade or toggled on/off (depending on `is_toggle_test`). @@ -383,10 +381,10 @@ def compare_active_and_reactive_power_pre_post( result_manager.warning( f"Column '{reactive_power_col}' not found, skipping reactive vs active power plot for {wtg_name}" ) - return + return None if is_toggle_test: - plot_toggle_active_vs_reactive_power( + return plot_toggle_active_vs_reactive_power( input_df=pd.concat([pre_df, post_df]), wtg_name=wtg_name, toggle_name="toggle", @@ -395,12 +393,11 @@ def compare_active_and_reactive_power_pre_post( plot_cfg=plot_cfg, sub_dir=sub_dir, ) - return pre_df_fake_toggle = pre_df.copy() post_df_fake_toggle = post_df.copy() pre_df_fake_toggle["test_toggle_off"] = True post_df_fake_toggle["test_toggle_on"] = True - plot_toggle_active_vs_reactive_power( + return plot_toggle_active_vs_reactive_power( input_df=pd.concat([pre_df_fake_toggle, post_df_fake_toggle]), wtg_name=wtg_name, toggle_name="upgrade", @@ -409,7 +406,6 @@ def compare_active_and_reactive_power_pre_post( plot_cfg=plot_cfg, sub_dir=sub_dir, ) - return def compare_ops_curves_pre_post( From abab836a26973b1b2a3e1987e655dbaf5041d205 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Tue, 12 May 2026 07:48:36 +0100 Subject: [PATCH 14/16] guard None --- wind_up/main_analysis.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/wind_up/main_analysis.py b/wind_up/main_analysis.py index 141ca75..a73fdbd 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -595,7 +595,8 @@ def _calc_test_ref_results( sub_dir=f"{test_name}/{ref_name}", is_toggle_test=cfg.toggle is not None, ) - plt.close(_fig) + if _fig is not None: + plt.close(_fig) ref_ops_curve_shift_dict = _check_for_ops_curve_shift( pre_df, @@ -891,7 +892,8 @@ def run_wind_up_analysis( plot_cfg=plot_cfg, is_toggle_test=cfg.toggle is not None, ) - plt.close(_fig) + if _fig is not None: + plt.close(_fig) test_ops_curve_shift_dict = _check_for_ops_curve_shift( pre_df, From 6ef2076c6c2abfe3d56932cf4c85f0fe08ca2423 Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Tue, 12 May 2026 09:28:38 +0100 Subject: [PATCH 15/16] fix lint issues --- pyproject.toml | 4 ++-- wind_up/main_analysis.py | 12 ++++++------ wind_up/plots/ws_est_plots.py | 2 +- wind_up/plots/yaw_direction_plots.py | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9cd8d1a..da7252b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "res-wind-up" -version = "0.4.9" +version = "0.4.10" 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 = 68 # try to bring this down to 50 +max-statements = 69 # 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 a73fdbd..91ee8af 100644 --- a/wind_up/main_analysis.py +++ b/wind_up/main_analysis.py @@ -585,7 +585,7 @@ def _calc_test_ref_results( pre_df = pre_df.merge(ref_df, how="left", left_index=True, right_index=True) post_df = post_df.merge(ref_df, how="left", left_index=True, right_index=True) - _fig = compare_active_and_reactive_power_pre_post( + _reactive_fig = compare_active_and_reactive_power_pre_post( pre_df=pre_df, post_df=post_df, wtg_name=ref_name, @@ -595,8 +595,8 @@ def _calc_test_ref_results( sub_dir=f"{test_name}/{ref_name}", is_toggle_test=cfg.toggle is not None, ) - if _fig is not None: - plt.close(_fig) + if _reactive_fig is not None: + plt.close(_reactive_fig) ref_ops_curve_shift_dict = _check_for_ops_curve_shift( pre_df, @@ -883,7 +883,7 @@ def run_wind_up_analysis( test_df, pre_df, post_df = pre_post_splitter.split(test_df, test_wtg_name=test_name) - _fig = compare_active_and_reactive_power_pre_post( + _reactive_fig = compare_active_and_reactive_power_pre_post( pre_df=pre_df, post_df=post_df, wtg_name=test_name, @@ -892,8 +892,8 @@ def run_wind_up_analysis( plot_cfg=plot_cfg, is_toggle_test=cfg.toggle is not None, ) - if _fig is not None: - plt.close(_fig) + if _reactive_fig is not None: + plt.close(_reactive_fig) test_ops_curve_shift_dict = _check_for_ops_curve_shift( pre_df, diff --git a/wind_up/plots/ws_est_plots.py b/wind_up/plots/ws_est_plots.py index 4ab9d68..6e36ec5 100644 --- a/wind_up/plots/ws_est_plots.py +++ b/wind_up/plots/ws_est_plots.py @@ -35,7 +35,7 @@ def plot_ws_est_gain_xs_one_ttype( plt.close() -def plot_ws_est_one_ttype_or_wtg( # noqa C901 PLR0915 +def plot_ws_est_one_ttype_or_wtg( df: pd.DataFrame, ttype_or_wtg: str, pc_transposed: pd.DataFrame, diff --git a/wind_up/plots/yaw_direction_plots.py b/wind_up/plots/yaw_direction_plots.py index 7c18553..16c0e6b 100644 --- a/wind_up/plots/yaw_direction_plots.py +++ b/wind_up/plots/yaw_direction_plots.py @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) -def plot_yaw_direction_pre_post_per_signal( # noqa:PLR0915 +def plot_yaw_direction_pre_post_per_signal( signal_name: str, test_wd_col: str, *, From fa67e36b95355745bec3a1bcc4d0821211525b4f Mon Sep 17 00:00:00 2001 From: Alex Clerc Date: Tue, 12 May 2026 11:46:18 +0100 Subject: [PATCH 16/16] improve plot_combined_results --- wind_up/plots/combine_results_plots.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/wind_up/plots/combine_results_plots.py b/wind_up/plots/combine_results_plots.py index 7e274e5..299774f 100644 --- a/wind_up/plots/combine_results_plots.py +++ b/wind_up/plots/combine_results_plots.py @@ -14,21 +14,23 @@ def plot_combined_results(tdf: pd.DataFrame, *, plot_cfg: PlotConfig, confidence grouped_results = tdf.index.name == "role" plt.figure() - labels = tdf.index.to_list() if grouped_results else tdf["test_wtg"] + labels = ( + [f"{i} ({int(v)} wtgs)" for i, v in zip(tdf.index, tdf["wtg_count"])] if grouped_results else tdf["test_wtg"] + ) values = tdf["p50_uplift"] * 100 yerrs = tdf["sigma"] * 100 * z_score plt.bar(labels, values, yerr=yerrs, capsize=3) plt.xlabel("turbine group" if grouped_results else "turbine") - plt.ylabel("uplift [%]") + plt.ylabel("uplift [%]", fontsize=12) plot_title = f"combined uplift and {confidence * 100:.0f}% CI" plt.title(plot_title) plt.grid(axis="y") - plt.xticks(rotation=90, ha="right") + plt.xticks(fontsize=12) plt.tight_layout() if show_plots: plt.show() if save_plots: - plt.savefig(plot_cfg.plots_dir / f"{plot_title}.png") + plt.savefig(plot_cfg.plots_dir / f"{plot_title.replace('%', 'pct')}.png") plt.close()