From 9d32caaded482cfb7807b8f965e4ac0fa17f5719 Mon Sep 17 00:00:00 2001 From: GustaafL <41048720+GustaafL@users.noreply.github.com> Date: Thu, 7 Dec 2023 12:32:45 +0100 Subject: [PATCH] Backport PR #918: fix(logs):reduce log messages from schedule (#918) * fix(logs):reduce log messages from schedule Signed-off-by: GustaafL * docs(changelog): add changelog entry Signed-off-by: GustaafL * refactor(imports): remove whitespaces Signed-off-by: GustaafL * docs(changelog): move fix to 0.17.1 instead of 0.18.0 Signed-off-by: GustaafL * fix(scheduling): fix typo in log message Signed-off-by: GustaafL * feat(test): add entry within the window of the schedule Signed-off-by: GustaafL * feat(schedule): add different log message for single target out of window datetime Signed-off-by: GustaafL --------- Signed-off-by: GustaafL --- documentation/changelog.rst | 1 + flexmeasures/data/models/planning/storage.py | 17 +++++--- .../data/models/planning/tests/test_solver.py | 42 +++++++++++++++++++ .../data/schemas/scheduling/storage.py | 1 - 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/documentation/changelog.rst b/documentation/changelog.rst index b83f64d46..e0454493f 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -20,6 +20,7 @@ v0.17.1 | November 22, 2023 Bugfixes ----------- +* Reduce worker logs when datetime exceeds the end of the schedule [see `PR #918 `_] * Fix infeasible problem due to incorrect estimation of the big-M value [see `PR #905 `_] * Fix API version listing (GET /api/v3_0) for hosts running on Python 3.8 [see `PR #917 `_] diff --git a/flexmeasures/data/models/planning/storage.py b/flexmeasures/data/models/planning/storage.py index 7759b50f5..5b47ff863 100644 --- a/flexmeasures/data/models/planning/storage.py +++ b/flexmeasures/data/models/planning/storage.py @@ -480,7 +480,6 @@ def ensure_soc_min_max(self): class StorageFallbackScheduler(MetaStorageScheduler): - __version__ = "1" __author__ = "Seita" @@ -630,6 +629,7 @@ def build_device_soc_values( if isinstance(soc_values, pd.Series): # some tests prepare it this way device_values = soc_values else: + disregarded_datetimes = [] device_values = initialize_series( np.nan, start=start_of_schedule, @@ -645,14 +645,22 @@ def build_device_soc_values( ) # otherwise DST would be problematic if soc_datetime > end_of_schedule: # Skip too-far-into-the-future target + disregarded_datetimes += [soc_datetime] max_server_horizon = get_max_planning_horizon(resolution) - current_app.logger.warning( - f"Disregarding target datetime {soc_datetime}, because it exceeds {end_of_schedule}. Maximum scheduling horizon is {max_server_horizon}." - ) continue device_values.loc[soc_datetime] = soc + if disregarded_datetimes: + if len(disregarded_datetimes) == 1: + current_app.logger.warning( + f"Disregarding 1 target datetime {disregarded_datetimes[0]}, because it exceeds {end_of_schedule}. Maximum scheduling horizon is {max_server_horizon}." + ) + else: + current_app.logger.warning( + f"Disregarding {len(disregarded_datetimes)} target datetimes from {min(disregarded_datetimes)} until {max(disregarded_datetimes)}, because they exceed {end_of_schedule}. Maximum scheduling horizon is {max_server_horizon}." + ) + # soc_values are at the end of each time slot, while prices are indexed by the start of each time slot device_values = device_values[start_of_schedule + resolution : end_of_schedule] @@ -904,7 +912,6 @@ def sanitize_expression(expression: str, columns: list) -> tuple[str, list]: columns_involved = [] for column in columns: - if re.search(get_pattern_match_word(column), _expression): columns_involved.append(column) diff --git a/flexmeasures/data/models/planning/tests/test_solver.py b/flexmeasures/data/models/planning/tests/test_solver.py index d1e4c1a92..dcd8f0f1f 100644 --- a/flexmeasures/data/models/planning/tests/test_solver.py +++ b/flexmeasures/data/models/planning/tests/test_solver.py @@ -1,6 +1,7 @@ from datetime import datetime, timedelta import pytest import pytz +import logging import numpy as np import pandas as pd @@ -13,6 +14,7 @@ StorageScheduler, add_storage_constraints, validate_storage_constraints, + build_device_soc_values, ) from flexmeasures.data.models.planning.linear_optimization import device_scheduler from flexmeasures.data.models.planning.tests.utils import check_constraints @@ -1255,3 +1257,43 @@ def set_if_not_none(dictionary, key, value): assert all(ems_constraints["derivative min"] == expected_site_production_capacity) assert all(ems_constraints["derivative max"] == expected_site_consumption_capacity) + + +@pytest.mark.parametrize( + ["soc_values", "log_message"], + [ + ( + [ + {"datetime": datetime(2023, 5, 19, tzinfo=pytz.utc), "value": 1.0}, + {"datetime": datetime(2023, 5, 22, tzinfo=pytz.utc), "value": 1.0}, + {"datetime": datetime(2023, 5, 23, tzinfo=pytz.utc), "value": 1.0}, + {"datetime": datetime(2023, 5, 21, tzinfo=pytz.utc), "value": 1.0}, + ], + "Disregarding 3 target datetimes from 2023-05-21 00:00:00+00:00 until 2023-05-23 00:00:00+00:00, because they exceed 2023-05-20 00:00:00+00:00", + ), + ( + [ + {"datetime": datetime(2023, 5, 19, tzinfo=pytz.utc), "value": 1.0}, + {"datetime": datetime(2023, 5, 23, tzinfo=pytz.utc), "value": 1.0}, + ], + "Disregarding 1 target datetime 2023-05-23 00:00:00+00:00, because it exceeds 2023-05-20 00:00:00+00:00", + ), + ], +) +def test_build_device_soc_values(caplog, soc_values, log_message): + caplog.set_level(logging.WARNING) + soc_at_start = 3.0 + start_of_schedule = datetime(2023, 5, 18, tzinfo=pytz.utc) + end_of_schedule = datetime(2023, 5, 20, tzinfo=pytz.utc) + resolution = timedelta(minutes=5) + + with caplog.at_level(logging.WARNING): + device_values = build_device_soc_values( + soc_values=soc_values, + soc_at_start=soc_at_start, + start_of_schedule=start_of_schedule, + end_of_schedule=end_of_schedule, + resolution=resolution, + ) + print(device_values) + assert log_message in caplog.text diff --git a/flexmeasures/data/schemas/scheduling/storage.py b/flexmeasures/data/schemas/scheduling/storage.py index 1c5ba9c83..f05166514 100644 --- a/flexmeasures/data/schemas/scheduling/storage.py +++ b/flexmeasures/data/schemas/scheduling/storage.py @@ -60,7 +60,6 @@ def __init__(self, *args, **kwargs): @validates("value") def validate_value(self, _value): - if self.value_validator is not None: self.value_validator(_value)