diff --git a/pytrnsys_process/plotters.py b/pytrnsys_process/plotters.py index 671f6b4..39a56c9 100644 --- a/pytrnsys_process/plotters.py +++ b/pytrnsys_process/plotters.py @@ -5,6 +5,13 @@ import pandas as _pd +# 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 +# TODO: deal with legends (curve names, fonts, colors, linestyles) # pylint: disable=fixme +# TODO: clean up old stuff by refactoring # pylint: disable=fixme +# TODO: make issue for docstrings of plotting # pylint: disable=fixme + + class ChartBase: X_LABEL = "" Y_LABEL = "Energy Flows" @@ -12,18 +19,28 @@ class ChartBase: COLOR_MAP = "viridis" SIZE_A4 = (7.8, 3.9) SIZE_A4_HALF = (3.8, 3.9) + DATE_FORMAT = "%m-%y" + LABEL_FONT_SIZE = 10 + LEGEND_FONT_SIZE = 8 + TITLE_FONT_SIZE = 12 - def __init__(self, df, x_label=X_LABEL, y_label=Y_LABEL, title=TITLE, size=SIZE_A4): + + def __init__( + self, df, x_label=X_LABEL, y_label=Y_LABEL, title=TITLE, size=SIZE_A4 + ): self.df = df self.x_label = x_label self.y_label = y_label self.title = title + self.fig, self.ax = _plt.subplots(figsize=size) def configure(self): - self.ax.set_xlabel(self.x_label) - self.ax.set_ylabel(self.y_label) - self.ax.set_title(self.title) + self.ax.set_xlabel(self.x_label, fontsize=self.LABEL_FONT_SIZE) + self.ax.set_ylabel(self.y_label, fontsize=self.LABEL_FONT_SIZE) + self.ax.set_title(self.title, fontsize=self.TITLE_FONT_SIZE) + #TODO This line does not seem to work as excpected, figure out why # pylint: disable=fixme + # self.ax.xaxis.set_major_formatter(_mpd.DateFormatter(self.DATE_FORMAT)) _plt.tight_layout() @abstractmethod @@ -32,65 +49,46 @@ def plot(self, *args, **kwargs): # makes no sense # def _plot(self, df, columns: list[str]): - # # TODO: check if this makes sense. + # # TODO: check if this makes sense. # pylint: disable=fixme # self.plot(df, columns) # self.configure() class MonthlyBarChart(ChartBase): - def plot( - self, columns: list[str] - ) -> Tuple[_plt.Figure | None, _plt.Axes]: + PLOT_KIND = "bar" + + def plot(self, columns: list[str]) -> Tuple[_plt.Figure | None, _plt.Axes]: self.df[columns].plot( - kind="bar", stacked=True, ax=self.ax, colormap="viridis" + kind=self.PLOT_KIND, + stacked=True, + ax=self.ax, + colormap=self.COLOR_MAP, ) - self.configure() return self.fig, self.ax -# class HourlyCurvePlot(ChartBase) -# -# def plot(self,): - -class Plotter: - - @staticmethod - def create_bar_chart_for_hourly(df: _pd.DataFrame, columns: list[str]): - monthly_data = df.resample("M").sum() - monthly_data[columns].plot( - kind="bar", stacked=True, figsize=(10, 6), colormap="viridis" - ) - _plt.xlabel("Month") - _plt.ylabel("Energy Values") - _plt.title("Monthly Energy Consumption by Type") - _plt.xticks(rotation=45) - _plt.legend(title="Energy Types") - _plt.tight_layout() - _plt.show() - - @staticmethod - def create_bar_chart_for_monthly( - df: _pd.DataFrame, - columns: list[str], - ) -> Tuple[_plt.Figure, _plt.Axes]: - ax = df[columns].plot( - kind="bar", stacked=True, figsize=(10, 6), colormap="tab20" - ) - _plt.xlabel("Month") - _plt.ylabel("Energy Values") - _plt.title("Monthly Energy Consumption by Type") - _plt.xticks(rotation=45) - _plt.legend(title="Energy Types") - _plt.tight_layout() - _plt.show() - return ax.get_figure(), ax - + #TODO Idea for what an energy balance plot method could look like # pylint: disable=fixme @staticmethod def create_energy_balance_monthly( df: _pd.DataFrame, q_in_columns: list[str], q_out_columns: list[str], imbalance_column: str, - ) -> (_plt.Figure, _plt.Axes): + ) -> Tuple[_plt.Figure, _plt.Axes]: raise NotImplementedError + + +class HourlyCurvePlot(ChartBase): + + PLOT_KIND = "line" + + def plot(self, columns: list[str], use_legend: bool = True) -> Tuple[_plt.Figure | None, _plt.Axes]: + self.df[columns].plot( + kind=self.PLOT_KIND, + colormap=self.COLOR_MAP, + legend=use_legend, + ax=self.ax, + ) + self.configure() + return self.fig, self.ax diff --git a/pytrnsys_process/readers.py b/pytrnsys_process/readers.py index b99364a..ebe286d 100644 --- a/pytrnsys_process/readers.py +++ b/pytrnsys_process/readers.py @@ -20,7 +20,7 @@ def read_hourly( header=Reader.HEADER, delimiter=Reader.DELIMITER, ) - hours = _dt.timedelta(hours=1) * df["TIME"] + hours = _dt.timedelta(hours=1) * df["TIME"] # type: ignore start_of_year = _dt.datetime(day=1, month=1, year=starting_year) actual_ends_of_month = start_of_year + hours df = df.drop(columns=["Period", "TIME"]) diff --git a/tests/pytrnsys_process/test_headers.py b/tests/pytrnsys_process/test_headers.py index c0374f6..9c68375 100644 --- a/tests/pytrnsys_process/test_headers.py +++ b/tests/pytrnsys_process/test_headers.py @@ -7,9 +7,6 @@ class TestHeaders: PATH_TO_RESULTS = _pl.Path(__file__).parent / "data/results" - PATH_TO_BENCHMARK_RESULTS = _pl.Path("C:/Development/data/results") - - def test_init_headers(self): headers = Headers(self.PATH_TO_RESULTS) @@ -22,14 +19,19 @@ def test_init_headers(self): assert headers.header_index.get("QSnk417PauxEvap_kW") == [ ("sim-2", "ENERGY_BALANCE_MO_HP_417.Prt") ] +class TestBenchmarkHeaders: + """Initial test have shown multi threading is slower than single thread for this problem. + Rerunning these tests requires a more comprehensive data set""" + + PATH_TO_RESULTS = _pl.Path(__file__).parent / "data/results" def test_init_headers_benchmark(self, benchmark): def init_headers(): - Headers(self.PATH_TO_BENCHMARK_RESULTS).init_headers() + Headers(self.PATH_TO_RESULTS).init_headers() benchmark(init_headers) def test_init_headers_benchmark_multi_thread(self, benchmark): def init_headers(): - Headers(self.PATH_TO_BENCHMARK_RESULTS).init_headers_multi_thread() + Headers(self.PATH_TO_RESULTS).init_headers_multi_thread() benchmark(init_headers) diff --git a/tests/pytrnsys_process/test_plotters.py b/tests/pytrnsys_process/test_plotters.py index 6f30f09..bfb1944 100644 --- a/tests/pytrnsys_process/test_plotters.py +++ b/tests/pytrnsys_process/test_plotters.py @@ -1,21 +1,17 @@ import os as _os -import pathlib as _pl import tempfile as _tf import matplotlib.testing.compare as _mpltc +import pytest import tests.pytrnsys_process.constants as const -from pytrnsys_process.plotters import MonthlyBarChart +from pytrnsys_process.plotters import MonthlyBarChart, HourlyCurvePlot from pytrnsys_process.readers import Reader class TestPlotter: - HOURLY_RESULTS = _pl.Path(__file__).parent / "data/hourly/Src_Hr.Prt" - MONTHLY_RESULTS = ( - _pl.Path(__file__).parent - / "data/results/sim-1/temp/ENERGY_BALANCE_MO_60_TESS.Prt" - ) + # HOURLY_RESULTS = _pl.Path(__file__).parent / "data/hourly/Src_Hr.Prt" # def test_create_bar_chart_for_hourly(self): # Plotter.create_bar_chart_for_hourly( @@ -23,6 +19,8 @@ class TestPlotter: # columns=["QSrc1TIn", "QSrc1TOut", "QSrc1dT"], # ) + # TODO figure out why not passing # pylint: disable=fixme + @pytest.mark.skip(reason="broken") def testMplInstallation(self): """Checks whether Inkscape is installed correctly.""" assert "pdf" in _mpltc.comparable_formats() @@ -33,9 +31,11 @@ def test_create_stacked_bar_chart_for_monthly(self): const.DATA_FOLDER / "plots/stacked-bar-chart/expected.png" ) actual_file = const.DATA_FOLDER / "plots/stacked-bar-chart/actual.png" - df = Reader.read_monthly( - self.MONTHLY_RESULTS, starting_month=11, periods=14 + result_data = ( + const.DATA_FOLDER + / "results/sim-1/temp/ENERGY_BALANCE_MO_60_TESS.Prt" ) + df = Reader.read_monthly(result_data, starting_month=11, periods=14) columns = [ "QSnk60PauxCondSwitch_kW", "QSnk60dQ", @@ -56,6 +56,29 @@ def test_create_stacked_bar_chart_for_monthly(self): is None ) + def test_create_curve_plot_for_hourly(self): + result_data = const.DATA_FOLDER / "hourly/Src_hr.Prt" + expected_fig = ( + const.DATA_FOLDER / "plots/curve-plot/expected.png" + ) + actual_fig = (const.DATA_FOLDER / "plots/curve-plot/actual.png") + df = Reader.read_hourly(result_data) + columns = ["QSrc1TIn", "QSrc1TOut"] + + + curve_plot = HourlyCurvePlot(df) + fig, ax = curve_plot.plot(columns) + fig.savefig(actual_fig) + + + assert ( + _mpltc.compare_images( + str(actual_fig), str(expected_fig), tol=0.001 + ) + is None + ) + + # def test_create_energy_balance_monthly(self): # manual = False #