From e903183ed8b6fae8b40707080ce2e01d2d827c87 Mon Sep 17 00:00:00 2001 From: Kilian Helfenbein Date: Fri, 4 Nov 2022 13:29:20 +0100 Subject: [PATCH 1/9] if the step size of the time series of the edisgo object differs from the simbev step size, the edisgo time series is now resampled when apllying an charging strategy --- edisgo/edisgo.py | 5 +++++ edisgo/flex_opt/charging_strategies.py | 15 +++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/edisgo/edisgo.py b/edisgo/edisgo.py index 55797eb97..18b19b1a5 100755 --- a/edisgo/edisgo.py +++ b/edisgo/edisgo.py @@ -1559,6 +1559,11 @@ def apply_charging_strategy(self, strategy="dumb", **kwargs): in :attr:`~.network.timeseries.TimeSeries.loads_reactive_power` is set to 0 Mvar. + If the time series of the edisgo object and the SimBEV data are provided with + different time steps, then the time series of the edisgo object are + automatically resampled to match the SimBEV data. See + :func:`edisgo.apply_charging_strategy`. + Parameters ---------- strategy : str diff --git a/edisgo/flex_opt/charging_strategies.py b/edisgo/flex_opt/charging_strategies.py index 9946fc227..91e0e2d8f 100644 --- a/edisgo/flex_opt/charging_strategies.py +++ b/edisgo/flex_opt/charging_strategies.py @@ -107,12 +107,15 @@ def charging_strategy( ) simbev_timedelta = timeindex[1] - timeindex[0] - assert edisgo_timedelta == simbev_timedelta, ( - "The step size of the time series of the edisgo object differs from the" - f"simbev step size. The edisgo time delta is {edisgo_timedelta}, while" - f" the simbev time delta is {simbev_timedelta}. Make sure to use a " - f"matching step size." - ) + if edisgo_timedelta != simbev_timedelta: + logger.warning( + "The step size of the time series of the edisgo object differs from the" + f"simbev step size. The edisgo time delta is {edisgo_timedelta}, while" + f" the simbev time delta is {simbev_timedelta}. The edisgo time series " + f"will be resampled accordingly." + ) + + edisgo_obj.resample_timeseries(freq=f"{edisgo_obj.electromobility.stepsize}Min") if strategy == "dumb": # "dumb" charging From a7dbd807d5a031c58e5c1dc092a897ff892ff3e7 Mon Sep 17 00:00:00 2001 From: Kilian Helfenbein Date: Fri, 4 Nov 2022 13:33:49 +0100 Subject: [PATCH 2/9] test new code fragment; added type hinting --- edisgo/flex_opt/charging_strategies.py | 16 ++++++++++++---- tests/flex_opt/test_charging_strategy.py | 1 - 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/edisgo/flex_opt/charging_strategies.py b/edisgo/flex_opt/charging_strategies.py index 91e0e2d8f..065b91737 100644 --- a/edisgo/flex_opt/charging_strategies.py +++ b/edisgo/flex_opt/charging_strategies.py @@ -1,8 +1,16 @@ +from __future__ import annotations + import logging +from numbers import Number +from typing import TYPE_CHECKING + import numpy as np import pandas as pd +if TYPE_CHECKING: + from edisgo import EDisGo + RELEVANT_CHARGING_STRATEGIES_COLUMNS = { "dumb": [ "park_start_timesteps", @@ -40,10 +48,10 @@ # wrong results if the timeindex of the edisgo object is not continuously # (e.g. 2 weeks of the year) def charging_strategy( - edisgo_obj, - strategy="dumb", - timestamp_share_threshold=0.2, - minimum_charging_capacity_factor=0.1, + edisgo_obj: EDisGo, + strategy: str = "dumb", + timestamp_share_threshold: Number = 0.2, + minimum_charging_capacity_factor: Number = 0.1, ): """ Applies charging strategy to set EV charging time series at charging parks. diff --git a/tests/flex_opt/test_charging_strategy.py b/tests/flex_opt/test_charging_strategy.py index 8908a30c2..4731138b4 100644 --- a/tests/flex_opt/test_charging_strategy.py +++ b/tests/flex_opt/test_charging_strategy.py @@ -23,7 +23,6 @@ def setup_class(cls): timeindex = pd.date_range("1/1/2011", periods=24 * 7, freq="H") cls.edisgo_obj.set_timeindex(timeindex) - cls.edisgo_obj.resample_timeseries() cls.edisgo_obj.import_electromobility(cls.simbev_path, cls.tracbev_path) def test_charging_strategy(self): From 3d85799962b09d2f39f10c2bd6b54d3ab0f71560 Mon Sep 17 00:00:00 2001 From: birgits Date: Wed, 9 Nov 2022 17:37:59 +0100 Subject: [PATCH 3/9] Adapt docstring --- edisgo/edisgo.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/edisgo/edisgo.py b/edisgo/edisgo.py index 18b19b1a5..7c7bc4a1e 100755 --- a/edisgo/edisgo.py +++ b/edisgo/edisgo.py @@ -1559,10 +1559,11 @@ def apply_charging_strategy(self, strategy="dumb", **kwargs): in :attr:`~.network.timeseries.TimeSeries.loads_reactive_power` is set to 0 Mvar. - If the time series of the edisgo object and the SimBEV data are provided with - different time steps, then the time series of the edisgo object are - automatically resampled to match the SimBEV data. See - :func:`edisgo.apply_charging_strategy`. + If the frequency of time series data in :class:`~.network.timeseries.TimeSeries` + (checked using :attr:`~.network.timeseries.timeindex`) differs from the + frequency of SimBEV data, then the time series in + :class:`~.network.timeseries.TimeSeries` automatically resampled to match the + SimBEV data frequency. Parameters ---------- From 25dca6e3f3778746fcc443b02a5bbbab25877b30 Mon Sep 17 00:00:00 2001 From: birgits Date: Wed, 9 Nov 2022 17:38:23 +0100 Subject: [PATCH 4/9] Add resampling test --- tests/network/test_timeseries.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/network/test_timeseries.py b/tests/network/test_timeseries.py index 9e276e1a6..cabd0047e 100644 --- a/tests/network/test_timeseries.py +++ b/tests/network/test_timeseries.py @@ -8,7 +8,11 @@ import pandas as pd import pytest -from pandas.util.testing import assert_frame_equal, assert_series_equal +from pandas.util.testing import ( + assert_frame_equal, + assert_index_equal, + assert_series_equal, +) from edisgo import EDisGo from edisgo.network import timeseries @@ -2317,6 +2321,7 @@ def test_resample_timeseries(self): len_timeindex_orig = len(self.edisgo.timeseries.timeindex) mean_value_orig = self.edisgo.timeseries.generators_active_power.mean() + index_orig = self.edisgo.timeseries.timeindex.copy() # test up-sampling self.edisgo.timeseries.resample_timeseries() @@ -2332,6 +2337,9 @@ def test_resample_timeseries(self): atol=1e-5, ) ).all() + # check if index is the same after resampled back + self.edisgo.timeseries.resample_timeseries(freq="1h") + assert_index_equal(self.edisgo.timeseries.timeindex, index_orig) # same tests for down-sampling self.edisgo.timeseries.resample_timeseries(freq="2h") From b7b31ad3eebf69ea57079c46211c441bb68833e5 Mon Sep 17 00:00:00 2001 From: Kilian Helfenbein Date: Tue, 3 Jan 2023 09:54:03 +0100 Subject: [PATCH 5/9] added re-resampling --- edisgo/edisgo.py | 4 +++- edisgo/flex_opt/charging_strategies.py | 13 ++++++++++--- edisgo/network/timeseries.py | 4 +++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/edisgo/edisgo.py b/edisgo/edisgo.py index 7c7bc4a1e..da9e654cd 100755 --- a/edisgo/edisgo.py +++ b/edisgo/edisgo.py @@ -2262,7 +2262,9 @@ def check_integrity(self): logging.info("Integrity check finished. Please pay attention to warnings.") - def resample_timeseries(self, method: str = "ffill", freq: str = "15min"): + def resample_timeseries( + self, method: str = "ffill", freq: str | pd.Timedelta = "15min" + ): """ Resamples all generator, load and storage time series to a desired resolution. diff --git a/edisgo/flex_opt/charging_strategies.py b/edisgo/flex_opt/charging_strategies.py index 065b91737..f607ef248 100644 --- a/edisgo/flex_opt/charging_strategies.py +++ b/edisgo/flex_opt/charging_strategies.py @@ -115,15 +115,19 @@ def charging_strategy( ) simbev_timedelta = timeindex[1] - timeindex[0] - if edisgo_timedelta != simbev_timedelta: + resample = edisgo_timedelta != simbev_timedelta + + if resample: logger.warning( "The step size of the time series of the edisgo object differs from the" f"simbev step size. The edisgo time delta is {edisgo_timedelta}, while" f" the simbev time delta is {simbev_timedelta}. The edisgo time series " - f"will be resampled accordingly." + f"will be resampled accordingly before applying the charging strategy. " + f"After applying the charging strategy the time series will be resampled " + f"to it's original state." ) - edisgo_obj.resample_timeseries(freq=f"{edisgo_obj.electromobility.stepsize}Min") + edisgo_obj.resample_timeseries(freq=simbev_timedelta) if strategy == "dumb": # "dumb" charging @@ -303,6 +307,9 @@ def charging_strategy( else: raise ValueError(f"Strategy {strategy} has not yet been implemented.") + if resample: + edisgo_obj.resample_timeseries(freq=edisgo_timedelta) + # set reactive power time series to 0 Mvar # fmt: off edisgo_obj.timeseries.add_component_time_series( diff --git a/edisgo/network/timeseries.py b/edisgo/network/timeseries.py index d5c58068b..7cba12179 100644 --- a/edisgo/network/timeseries.py +++ b/edisgo/network/timeseries.py @@ -2088,7 +2088,9 @@ def _check_if_components_exist( return set(component_names) - set(comps_not_in_network) return component_names - def resample_timeseries(self, method: str = "ffill", freq: str = "15min"): + def resample_timeseries( + self, method: str = "ffill", freq: str | pd.Timedelta = "15min" + ): """ Resamples all generator, load and storage time series to a desired resolution. From 94597e173c6d256f2f12f2ca2896fd4f38d59eb1 Mon Sep 17 00:00:00 2001 From: birgits Date: Tue, 10 Jan 2023 21:06:28 +0100 Subject: [PATCH 6/9] Fix docstring message on resampling and move text block to Notes section --- edisgo/edisgo.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/edisgo/edisgo.py b/edisgo/edisgo.py index da9e654cd..0f6faa9d9 100755 --- a/edisgo/edisgo.py +++ b/edisgo/edisgo.py @@ -1559,12 +1559,6 @@ def apply_charging_strategy(self, strategy="dumb", **kwargs): in :attr:`~.network.timeseries.TimeSeries.loads_reactive_power` is set to 0 Mvar. - If the frequency of time series data in :class:`~.network.timeseries.TimeSeries` - (checked using :attr:`~.network.timeseries.timeindex`) differs from the - frequency of SimBEV data, then the time series in - :class:`~.network.timeseries.TimeSeries` automatically resampled to match the - SimBEV data frequency. - Parameters ---------- strategy : str @@ -1607,6 +1601,15 @@ def apply_charging_strategy(self, strategy="dumb", **kwargs): capacity of 22 kW and a minimum_charging_capacity_factor of 0.1 this would result in a minimum charging power of 2.2 kW. Default: 0.1. + Notes + ------ + If the frequency of time series data in :class:`~.network.timeseries.TimeSeries` + (checked using :attr:`~.network.timeseries.TimeSeries.timeindex`) differs from + the frequency of SimBEV data, then the time series in + :class:`~.network.timeseries.TimeSeries` is first automatically resampled to + match the SimBEV data frequency and after determining the charging demand time + series resampled back to the original frequency. + """ charging_strategy(self, strategy=strategy, **kwargs) From 177fd807e573825f1c4d728105426c68f06a6b5d Mon Sep 17 00:00:00 2001 From: birgits Date: Tue, 10 Jan 2023 21:07:54 +0100 Subject: [PATCH 7/9] Minor changes to logging message --- edisgo/flex_opt/charging_strategies.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/edisgo/flex_opt/charging_strategies.py b/edisgo/flex_opt/charging_strategies.py index f607ef248..b792242c8 100644 --- a/edisgo/flex_opt/charging_strategies.py +++ b/edisgo/flex_opt/charging_strategies.py @@ -119,12 +119,13 @@ def charging_strategy( if resample: logger.warning( - "The step size of the time series of the edisgo object differs from the" - f"simbev step size. The edisgo time delta is {edisgo_timedelta}, while" - f" the simbev time delta is {simbev_timedelta}. The edisgo time series " + f"The frequency of the time series data of the edisgo object differs from " + f"the simbev time series frequency. The edisgo frequency is " + f"{edisgo_timedelta}, while the simbev frequency is {simbev_timedelta}. " + f"The edisgo time series data " f"will be resampled accordingly before applying the charging strategy. " - f"After applying the charging strategy the time series will be resampled " - f"to it's original state." + f"After applying the charging strategy all time series will be resampled " + f"to the original frequency of the edisgo time series data." ) edisgo_obj.resample_timeseries(freq=simbev_timedelta) From 68f9fd61479c142c6da2a28daa4a553eddbd5ab9 Mon Sep 17 00:00:00 2001 From: birgits Date: Tue, 10 Jan 2023 21:08:21 +0100 Subject: [PATCH 8/9] Expand charging strategy test to check if resampling works as expected --- tests/flex_opt/test_charging_strategy.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/tests/flex_opt/test_charging_strategy.py b/tests/flex_opt/test_charging_strategy.py index 4731138b4..c0165fc79 100644 --- a/tests/flex_opt/test_charging_strategy.py +++ b/tests/flex_opt/test_charging_strategy.py @@ -25,14 +25,14 @@ def setup_class(cls): cls.edisgo_obj.import_electromobility(cls.simbev_path, cls.tracbev_path) - def test_charging_strategy(self): + def test_charging_strategy(self, caplog): charging_demand_lst = [] + ts = self.edisgo_obj.timeseries + for strategy in self.charging_strategies: charging_strategy(self.edisgo_obj, strategy=strategy) - ts = self.edisgo_obj.timeseries - # Check if all charging points have a valid chargingdemand_kWh > 0 df = ts.charging_points_active_power(self.edisgo_obj).loc[ :, (ts.charging_points_active_power(self.edisgo_obj) <= 0).any(axis=0) @@ -49,7 +49,8 @@ def test_charging_strategy(self): self.edisgo_obj, strategy="dumb", timestamp_share_threshold=0.5 ) - ts = self.edisgo_obj.timeseries + # Check if resampling warning is raised + assert "The frequency of the time series data of the edisgo object differs" in caplog.text # Check if all charging points have a valid chargingdemand_kWh > 0 df = ts.charging_points_active_power(self.edisgo_obj).loc[ @@ -63,8 +64,6 @@ def test_charging_strategy(self): self.edisgo_obj, strategy="reduced", minimum_charging_capacity_factor=0.5 ) - ts = self.edisgo_obj.timeseries - # Check if all charging points have a valid chargingdemand_kWh > 0 df = ts.charging_points_active_power(self.edisgo_obj).loc[ :, (ts.charging_points_active_power(self.edisgo_obj) <= 0).any(axis=0) @@ -82,3 +81,13 @@ def test_charging_strategy(self): (_.round(4) == charging_demand_lst[0].round(4)).all() for _ in charging_demand_lst ) + + # ##################### check time index ##################### + assert ts._loads_active_power.index.freqstr == "H" + # change time index to quarter-hourly + timeindex = pd.date_range("1/1/2011", periods=24 * 7, freq="0.25H") + self.edisgo_obj.set_timeindex(timeindex) + charging_strategy( + self.edisgo_obj, strategy="dumb" + ) + assert ts._loads_active_power.index.freqstr == "15T" From 9a7e2678cf427fb9d520bea28a9457aacbab55eb Mon Sep 17 00:00:00 2001 From: birgits Date: Tue, 10 Jan 2023 21:08:49 +0100 Subject: [PATCH 9/9] Fix sphinx requirements to be the same as in rtd_requirements --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c861120d2..95946bc97 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,8 @@ def read(fname): dev_requirements = [ "pytest", "pytest-notebook", - "sphinx_rtd_theme", + "sphinx >= 4.3.0, < 5.1.0", + "sphinx_rtd_theme >=0.5.2", "sphinx-autodoc-typehints", "pre-commit", "black",