Skip to content

Commit

Permalink
use resolution to convert energy series to the target event resolution
Browse files Browse the repository at this point in the history
Signed-off-by: Victor Garcia Reolid <victor@seita.nl>
  • Loading branch information
victorgarcia98 committed Nov 24, 2023
1 parent 4dc808c commit c8af88f
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 28 deletions.
15 changes: 7 additions & 8 deletions flexmeasures/data/models/planning/linear_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def device_scheduler( # noqa C901
derivative equals: exact amount of flow (we do this by clamping derivative min and derivative max)
derivative down efficiency: conversion efficiency of flow out of a device (flow out : stock decrease)
derivative up efficiency: conversion efficiency of flow into a device (stock increase : flow in)
usage forecast: predefined stock delta to apply to the storage device. Positive quantity cause an increase and negative decrease (stock increase : flow in)
gain: predefined stock delta to apply to the storage device. Positive quantity cause an increase and negative decrease (stock increase : flow in)
EMS constraints are on an EMS level. Handled constraints (listed by column name):
derivative max: maximum flow
derivative min: minimum flow
Expand Down Expand Up @@ -103,6 +103,11 @@ def device_scheduler( # noqa C901
-device_constraint["derivative min"].min(),
)

for i in range(len(device_constraints)):
if "gain" not in device_constraints[i].columns:
device_constraints[i]["gain"] = 0
device_constraints[i]["gain"] = device_constraints[i]["gain"].fillna(0)

# Turn prices per commitment into prices per commitment flow
if len(commitment_downwards_deviation_price) != 0:
if all(
Expand Down Expand Up @@ -227,13 +232,7 @@ def device_derivative_up_efficiency(m, d, j):
return eff

def device_usage_forecast(m, d, j):
try:
usage_forecast = device_constraints[d]["usage forecast"].iloc[j]
except KeyError:
return 0
if np.isnan(usage_forecast):
return 0
return usage_forecast
return device_constraints[d]["gain"].iloc[j]

model.up_price = Param(model.c, model.j, initialize=price_up_select)
model.down_price = Param(model.c, model.j, initialize=price_down_select)
Expand Down
27 changes: 15 additions & 12 deletions flexmeasures/data/models/planning/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import numpy as np
from flask import current_app


from flexmeasures import Sensor
from flexmeasures.data.models.planning import Scheduler, SchedulerOutputType
from flexmeasures.data.models.planning.linear_optimization import device_scheduler
from flexmeasures.data.models.planning.utils import (
Expand Down Expand Up @@ -64,7 +64,7 @@ class MetaStorageScheduler(Scheduler):
"derivative min",
"derivative down efficiency",
"derivative up efficiency",
"usage forecast",
"gain",
]

def compute_schedule(self) -> pd.Series | None:
Expand Down Expand Up @@ -241,20 +241,23 @@ def _prepare(self, skip_validation: bool = False) -> tuple: # noqa: C901
all_usage_forecast = []

for component in usage_forecast:
all_usage_forecast.append(
get_continous_series_sensor_or_quantity(
quantity_or_sensor=component,
actuator=sensor,
target_unit="MWh",
query_window=(start, end),
resolution=resolution,
beliefs_before=belief_time,
)
gain = get_continous_series_sensor_or_quantity(
quantity_or_sensor=component,
actuator=sensor,
target_unit="MWh",
query_window=(start, end),
resolution=resolution,
beliefs_before=belief_time,
)

if isinstance(component, Sensor):
gain *= resolution / component.event_resolution

all_usage_forecast.append(gain)
if len(all_usage_forecast) > 0:
all_usage_forecast = pd.concat(all_usage_forecast, axis=1)

device_constraints[0]["usage forecast"] = all_usage_forecast.sum(1)
device_constraints[0]["gain"] = all_usage_forecast.sum(1)

# Apply round-trip efficiency evenly to charging and discharging
device_constraints[0]["derivative down efficiency"] = (
Expand Down
33 changes: 29 additions & 4 deletions flexmeasures/data/models/planning/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,9 @@ def process(db, building, setup_sources) -> dict[str, Sensor]:


@pytest.fixture(scope="module")
def add_usage_forecast(db, add_battery_assets, setup_sources) -> Sensor:
def add_usage_forecast(db, add_battery_assets, setup_sources) -> dict[str, Sensor]:
"""
Set up a constant usage forecast
Set up a constant gain
"""

battery = add_battery_assets["Test battery"]
Expand All @@ -235,7 +235,7 @@ def add_usage_forecast(db, add_battery_assets, setup_sources) -> Sensor:
)

usage_forecast_sensor = Sensor(
name="usage forecast",
name="gain",
unit="MWh",
event_resolution=timedelta(minutes=15),
generic_asset=battery,
Expand All @@ -249,7 +249,32 @@ def add_usage_forecast(db, add_battery_assets, setup_sources) -> Sensor:
db, usage_forecast_sensor, usage_forecast, time_slots, setup_sources["Seita"]
)

return usage_forecast_sensor
time_slots = initialize_index(
start=pd.Timestamp("2015-01-01").tz_localize("Europe/Amsterdam"),
end=pd.Timestamp("2015-01-02").tz_localize("Europe/Amsterdam"),
resolution="1H",
)

usage_forecast_sensor_hourly = Sensor(
name="gain hourly",
unit="MWh",
event_resolution=timedelta(hours=1),
generic_asset=battery,
)
db.session.add(usage_forecast_sensor)
db.session.flush()

usage_forecast = [-battery.get_attribute("capacity_in_mw") * 4] * len(time_slots)

add_as_beliefs(
db,
usage_forecast_sensor_hourly,
usage_forecast,
time_slots,
setup_sources["Seita"],
)

return {"gain": usage_forecast_sensor, "gain hourly": usage_forecast_sensor_hourly}


def add_as_beliefs(db, sensor, values, time_slots, source):
Expand Down
13 changes: 9 additions & 4 deletions flexmeasures/data/models/planning/tests/test_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -1377,7 +1377,10 @@ def test_battery_power_capacity_as_sensor(
assert all(device_constraints["derivative max"].values == expected_consumption)


def test_battery_usage_forecast_sensor(add_battery_assets, add_usage_forecast):
@pytest.mark.parametrize("usage_forecast_sensor", ["gain", "gain hourly"])
def test_battery_usage_forecast_sensor(
add_battery_assets, add_usage_forecast, usage_forecast_sensor
):
_, battery = get_sensors_from_db(add_battery_assets)
tz = pytz.timezone("Europe/Amsterdam")
start = tz.localize(datetime(2015, 1, 1))
Expand All @@ -1392,7 +1395,9 @@ def test_battery_usage_forecast_sensor(add_battery_assets, add_usage_forecast):
flex_model={
"soc-max": 2,
"soc-min": 0,
"usage-forecast": [{"sensor": add_usage_forecast.id}],
"usage-forecast": [
{"sensor": add_usage_forecast[usage_forecast_sensor].id}
],
"roundtrip-efficiency": 1,
"storage-efficiency": 1,
},
Expand Down Expand Up @@ -1430,6 +1435,6 @@ def test_battery_usage_forecast_quantity(
scheduler_info = scheduler._prepare()

if expected_usage_forecast is not None:
assert all(scheduler_info[5][0]["usage forecast"] == expected_usage_forecast)
assert all(scheduler_info[5][0]["gain"] == expected_usage_forecast)
else:
assert all(scheduler_info[5][0]["usage forecast"].isna())
assert all(scheduler_info[5][0]["gain"].isna())

0 comments on commit c8af88f

Please sign in to comment.