From b92f146f6a2e4fbbf63c7865f0bcf7a55a8a5d02 Mon Sep 17 00:00:00 2001 From: sebastian-swob Date: Mon, 16 Dec 2024 14:33:26 +0100 Subject: [PATCH] added energy balance changed how scatter is plotted --- pylintrc | 2 +- pytrnsys_process/plotting/plot_wrappers.py | 94 +++++++++++++++--- pytrnsys_process/plotting/plotters.py | 69 +++++-------- pytrnsys_process/settings.py | 9 ++ pytrnsys_process/utils.py | 10 +- .../data/plots/energy-balance/expected.png | Bin 0 -> 35724 bytes tests/pytrnsys_process/test_plotters.py | 40 +++++++- 7 files changed, 160 insertions(+), 64 deletions(-) create mode 100644 tests/pytrnsys_process/data/plots/energy-balance/expected.png diff --git a/pylintrc b/pylintrc index 57ac85e..2619d20 100644 --- a/pylintrc +++ b/pylintrc @@ -291,7 +291,7 @@ max-args=6 max-positional-arguments=6 # Maximum number of attributes for a class (see R0902). -max-attributes=7 +max-attributes=11 # Maximum number of boolean expressions in an if statement (see R0916). max-bool-expr=5 diff --git a/pytrnsys_process/plotting/plot_wrappers.py b/pytrnsys_process/plotting/plot_wrappers.py index 0c8d4ef..7ab7e2e 100644 --- a/pytrnsys_process/plotting/plot_wrappers.py +++ b/pytrnsys_process/plotting/plot_wrappers.py @@ -41,7 +41,7 @@ def line_plot( >>> >>> # run the single scenario on a single simulation >>> api.process_single_simulation( - >>> _pl.Path("data/results/complete-0-SnkScale0.6000-StoreScale8"), + >>> _pl.Path("path/to/single/simulation"), >>> create_line_plot, >>> ) @@ -87,7 +87,7 @@ def bar_chart( >>> >>> # run the single scenario on a single simulation >>> api.process_single_simulation( - >>> _pl.Path("data/results/complete-0-SnkScale0.6000-StoreScale8"), + >>> _pl.Path("path/to/single/simulation"), >>> create_bar_chart, >>> ) @@ -133,7 +133,7 @@ def stacked_bar_chart( >>> >>> # run the single scenario on a single simulation >>> api.process_single_simulation( - >>> _pl.Path("data/results/complete-0-SnkScale0.6000-StoreScale8"), + >>> _pl.Path("path/to/single/simulation"), >>> create_stacked_bar_chart, >>> ) @@ -179,7 +179,7 @@ def histogram( >>> >>> # run the single scenario on a single simulation >>> api.process_single_simulation( - >>> _pl.Path("data/results/complete-0-SnkScale0.6000-StoreScale8"), + >>> _pl.Path("path/to/single/simulation"), >>> create_histogram, >>> ) @@ -195,7 +195,6 @@ def histogram( def scatter_plot( df: _pd.DataFrame, - columns: list[str], x_column: str, y_column: str, use_legend: bool = True, @@ -206,7 +205,6 @@ def scatter_plot( Args: df: DataFrame containing the data to plot - columns: List of column names to plot x_column: Name of the column to use for x-axis values y_column: Name of the column to use for y-axis values use_legend: Whether to show the legend @@ -221,7 +219,6 @@ def scatter_plot( >>> def create_scatter_plot(simulation: api.Simulation): >>> fig, ax = api.scatter_plot( ... simulation.hourly, - ... columns=["var1", "var2", "var3"], ... x_column="var1", ... y_column="var2", ... ) @@ -233,7 +230,7 @@ def scatter_plot( >>> >>> # run the single scenario on a single simulation >>> api.process_single_simulation( - >>> _pl.Path("data/results/complete-0-SnkScale0.6000-StoreScale8"), + >>> _pl.Path("path/to/single/simulation"), >>> create_scatter_plot, >>> ) @@ -241,13 +238,84 @@ def scatter_plot( - Matplotlib documentation: https://matplotlib.org/stable/api/ - Pandas plotting: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.plot.scatter.html """ - plotter = pltrs.ScatterPlot() + fig, ax = _plt.subplots(figsize=size) + columns = [x_column, y_column] + df[columns].plot.scatter( + legend=use_legend, + ax=ax, + x=x_column, + y=y_column, + **kwargs, + ) + ax = pltrs.configure(ax) + + return fig, ax + + +def energy_balance( + df: _pd.DataFrame, + q_in_columns: list[str], + q_out_columns: list[str], + q_imb_column: _tp.Optional[str] = None, + use_legend: bool = True, + size: tuple[float, float] = const.PlotSizes.A4.value, + **kwargs: _tp.Any, +) -> tuple[_plt.Figure, _plt.Axes]: + """Create a stacked bar chart showing energy balance with inputs, outputs and imbalance. + + This function creates an energy balance visualization where: + - Input energies are shown as positive values + - Output energies are shown as negative values + - Energy imbalance is either provided or calculated as (sum of inputs + sum of outputs) + + Args: + df: DataFrame containing the energy data + q_in_columns: List of column names representing energy inputs + q_out_columns: List of column names representing energy outputs + q_imb_column: Optional column name containing pre-calculated energy imbalance + use_legend: Whether to show the legend + size: Figure size tuple (width, height) + **kwargs: Additional plotting arguments passed to the stacked bar chart + + Returns: + Tuple of (matplotlib Figure object, matplotlib Axes object) + + Example: + >>> from pytrnsys_process import api + >>> def create_energy_balance(simulation: api.Simulation): + >>> fig, ax = api.energy_balance( + ... simulation.monthly, + ... q_in_columns=['solar_gain', 'auxiliary_power'], + ... q_out_columns=['thermal_losses', 'consumption'], + ... ) + >>> ax.set_xlabel('Time') + >>> ax.set_ylabel('Energy [kWh]') + >>> ax.set_title('Monthly Energy Balance') + >>> ax.grid(True) + >>> # run the single scenario on a single simulation + >>> api.process_single_simulation( + >>> _pl.Path("path/to/single/simulation"), + >>> create_energy_balance, + >>> ) + """ + df_modified = df.copy() + + for col in q_out_columns: + df_modified[col] = -df_modified[col] + + if q_imb_column is None: + q_imb_column = "Qimb" + df_modified[q_imb_column] = df_modified[ + q_in_columns + q_out_columns + ].sum(axis=1) + + columns_to_plot = q_in_columns + q_out_columns + [q_imb_column] + + plotter = pltrs.StackedBarChart() return plotter.plot( - df, - columns, + df_modified, + columns_to_plot, use_legend=use_legend, size=size, - x=x_column, - y=y_column, **kwargs, ) diff --git a/pytrnsys_process/plotting/plotters.py b/pytrnsys_process/plotting/plotters.py index cd209ed..c13971e 100644 --- a/pytrnsys_process/plotting/plotters.py +++ b/pytrnsys_process/plotting/plotters.py @@ -8,7 +8,7 @@ import pytrnsys_process.constants as const import pytrnsys_process.headers as h - +from pytrnsys_process import settings as sett # TODO: provide A4 and half A4 plots to test sizes in latex # pylint: disable=fixme # TODO: provide height as input for plot? # pylint: disable=fixme @@ -18,23 +18,24 @@ # TODO: Add colormap support # pylint: disable=fixme +# TODO find a better place for this to live in # pylint : disable=fixme +plot_settings = sett.settings.plot + + +def configure(ax: _plt.Axes) -> _plt.Axes: + ax.set_xlabel( + plot_settings.x_label, fontsize=plot_settings.label_font_size + ) + ax.set_ylabel( + plot_settings.y_label, fontsize=plot_settings.label_font_size + ) + ax.set_title(plot_settings.title, fontsize=plot_settings.title_font_size) + _plt.tight_layout() + return ax + + @dataclass class ChartBase(h.HeaderValidationMixin): - X_LABEL = "" - Y_LABEL = "" - TITLE = "" - COLOR_MAP = "viridis" - DATE_FORMAT = "%b %Y" - LABEL_FONT_SIZE = 10 - LEGEND_FONT_SIZE = 8 - TITLE_FONT_SIZE = 12 - - def configure(self, ax: _plt.Axes) -> _plt.Axes: - ax.set_xlabel(self.X_LABEL, fontsize=self.LABEL_FONT_SIZE) - ax.set_ylabel(self.Y_LABEL, fontsize=self.LABEL_FONT_SIZE) - ax.set_title(self.TITLE, fontsize=self.TITLE_FONT_SIZE) - _plt.tight_layout() - return ax def plot( self, @@ -101,16 +102,16 @@ def _do_plot( fig, ax = _plt.subplots(figsize=size) plot_kwargs = { "stacked": True, - "colormap": self.COLOR_MAP, + "colormap": plot_settings.color_map, "legend": use_legend, "ax": ax, **kwargs, } ax = df[columns].plot.bar(**plot_kwargs) ax.set_xticklabels( - _pd.to_datetime(df.index).strftime(self.DATE_FORMAT) + _pd.to_datetime(df.index).strftime(plot_settings.date_format) ) - ax = self.configure(ax) + ax = configure(ax) return fig, ax @@ -147,10 +148,10 @@ def _do_plot( ax.set_xticks(x + width * (len(columns) - 1) / 2) ax.set_xticklabels( - _pd.to_datetime(df.index).strftime(self.DATE_FORMAT) + _pd.to_datetime(df.index).strftime(plot_settings.date_format) ) ax.tick_params(axis="x", labelrotation=90) - self.configure(ax) + configure(ax) return fig, ax @@ -166,13 +167,13 @@ def _do_plot( ) -> tuple[_plt.Figure, _plt.Axes]: fig, ax = _plt.subplots(figsize=size) plot_kwargs = { - "colormap": self.COLOR_MAP, + "colormap": plot_settings.color_map, "legend": use_legend, "ax": ax, **kwargs, } df[columns].plot.line(**plot_kwargs) - ax = self.configure(ax) + ax = configure(ax) return fig, ax @@ -190,30 +191,12 @@ def _do_plot( ) -> tuple[_plt.Figure, _plt.Axes]: fig, ax = _plt.subplots(figsize=size) plot_kwargs = { - "colormap": self.COLOR_MAP, + "colormap": plot_settings.color_map, "legend": use_legend, "ax": ax, "bins": self.bins, **kwargs, } df[columns].plot.hist(**plot_kwargs) - ax = self.configure(ax) - return fig, ax - - -class ScatterPlot(ChartBase): - def _do_plot( - self, - df: _pd.DataFrame, - columns: list[str], - use_legend: bool = True, - size: tuple[float, float] = const.PlotSizes.A4.value, - **kwargs: _tp.Any, - ) -> tuple[_plt.Figure, _plt.Axes]: - fig, ax = _plt.subplots(figsize=size) - # TODO: cleanup the other Plotters to remove the stringy dictionary. - df[columns].plot.scatter( - colormap=self.COLOR_MAP, legend=use_legend, ax=ax, **kwargs - ) - ax = self.configure(ax) + ax = configure(ax) return fig, ax diff --git a/pytrnsys_process/settings.py b/pytrnsys_process/settings.py index 75cf8a7..2cf45f0 100644 --- a/pytrnsys_process/settings.py +++ b/pytrnsys_process/settings.py @@ -25,6 +25,15 @@ class Plot: inkscape_path: str = "C://Program Files//Inkscape//bin//inkscape.exe" + x_label: str = "" + y_label: str = "" + title: str = "" + date_format: str = "%b %Y" + color_map: str = "viridis" + label_font_size: int = 10 + legend_font_size: int = 8 + title_font_size: int = 12 + @dataclass class Reader: diff --git a/pytrnsys_process/utils.py b/pytrnsys_process/utils.py index db430b7..015bda4 100644 --- a/pytrnsys_process/utils.py +++ b/pytrnsys_process/utils.py @@ -5,7 +5,7 @@ import matplotlib.pyplot as _plt -from pytrnsys_process import settings as _set +from pytrnsys_process import settings as sett from pytrnsys_process.logger import logger @@ -19,8 +19,8 @@ def get_sim_folders(path_to_results: _pl.Path) -> _abc.Sequence[_pl.Path]: def get_files( sim_folders: _abc.Sequence[_pl.Path], - results_folder_name: str = _set.settings.reader.folder_name_for_printer_files, - get_mfr_and_t: bool = _set.settings.reader.read_step_files, + results_folder_name: str = sett.settings.reader.folder_name_for_printer_files, + get_mfr_and_t: bool = sett.settings.reader.read_step_files, ) -> _abc.Sequence[_pl.Path]: sim_files: list[_pl.Path] = [] for sim_folder in sim_folders: @@ -74,7 +74,7 @@ def export_plots_in_configured_formats( >>> # etc. """ - plot_settings = _set.settings.plot + plot_settings = sett.settings.plot plots_folder = path_to_directory / "plots" plots_folder.mkdir(exist_ok=True) @@ -95,7 +95,7 @@ def export_plots_in_configured_formats( def convert_svg_to_emf(file_no_suffix: _pl.Path) -> None: try: - inkscape_path = _set.settings.plot.inkscape_path + inkscape_path = sett.settings.plot.inkscape_path if not _pl.Path(inkscape_path).exists(): raise OSError(f"Inkscape executable not found at: {inkscape_path}") emf_filepath = file_no_suffix.with_suffix(".emf") diff --git a/tests/pytrnsys_process/data/plots/energy-balance/expected.png b/tests/pytrnsys_process/data/plots/energy-balance/expected.png new file mode 100644 index 0000000000000000000000000000000000000000..f323041f9bd9ac866b8e05bb34e6962ffb1e48bd GIT binary patch literal 35724 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