-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(scheduling): Price devices distinctively in scheduling #654
base: main
Are you sure you want to change the base?
Changes from all commits
ab72c4f
a9550f0
bea996e
a80a475
014edfa
515c31b
c34952d
be79cf7
901b7f3
fc12afb
346030c
be387b5
f830cfc
e2fcb5a
7307256
0a9c6f0
9fe1831
a931838
f0919ae
41d3a2f
11d2b03
95da3f4
ea323eb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,6 +3,7 @@ | |
from flask import current_app | ||
import pandas as pd | ||
import numpy as np | ||
from typing import Dict | ||
from pandas.tseries.frequencies import to_offset | ||
from pyomo.core import ( | ||
ConcreteModel, | ||
|
@@ -29,8 +30,12 @@ def device_scheduler( # noqa C901 | |
device_constraints: List[pd.DataFrame], | ||
ems_constraints: pd.DataFrame, | ||
commitment_quantities: List[pd.Series], | ||
commitment_downwards_deviation_price: Union[List[pd.Series], List[float]], | ||
commitment_upwards_deviation_price: Union[List[pd.Series], List[float]], | ||
consumption_price_sensor_per_device: Dict[int, int], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed we should make this backwards compatible instead of introducing a change that potentially breaks things. At the moment, I think this constitutes a breaking change not only in terms of changing the function signature, but also in terms of breaking a use case. Namely, applying prices to the whole system rather than to each device individually. |
||
production_price_sensor_per_device: Dict[int, int], | ||
commitment_downwards_deviation_price_array: List[ | ||
Union[List[pd.Series], List[float]] | ||
], | ||
commitment_upwards_deviation_price_array: List[Union[List[pd.Series], List[float]]], | ||
) -> Tuple[List[pd.Series], float, SolverResults]: | ||
"""This generic device scheduler is able to handle an EMS with multiple devices, | ||
with various types of constraints on the EMS level and on the device level, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The docstring is not updated with the new parameters. |
||
|
@@ -54,10 +59,10 @@ def device_scheduler( # noqa C901 | |
Commitments are on an EMS level. Parameter explanations: | ||
commitment_quantities: amounts of flow specified in commitments (both previously ordered and newly requested) | ||
- e.g. in MW or boxes/h | ||
commitment_downwards_deviation_price: penalty for downwards deviations of the flow | ||
commitment_downwards_deviation_price_array: penalty for downwards deviations of the flows | ||
- e.g. in EUR/MW or EUR/(boxes/h) | ||
- either a single value (same value for each flow value) or a Series (different value for each flow value) | ||
commitment_upwards_deviation_price: penalty for upwards deviations of the flow | ||
commitment_upwards_deviation_price_array: penalty for upwards deviations of the flows | ||
|
||
All Series and DataFrames should have the same resolution. | ||
|
||
|
@@ -89,22 +94,26 @@ def device_scheduler( # noqa C901 | |
) | ||
|
||
# Turn prices per commitment into prices per commitment flow | ||
if len(commitment_downwards_deviation_price) != 0: | ||
if all( | ||
isinstance(price, float) for price in commitment_downwards_deviation_price | ||
): | ||
commitment_downwards_deviation_price = [ | ||
initialize_series(price, start, end, resolution) | ||
for price in commitment_downwards_deviation_price | ||
] | ||
if len(commitment_upwards_deviation_price) != 0: | ||
if all( | ||
isinstance(price, float) for price in commitment_upwards_deviation_price | ||
): | ||
commitment_upwards_deviation_price = [ | ||
initialize_series(price, start, end, resolution) | ||
for price in commitment_upwards_deviation_price | ||
] | ||
for i in range(0, len(commitment_downwards_deviation_price_array)): | ||
if len(commitment_downwards_deviation_price_array[i]) != 0: | ||
if all( | ||
isinstance(price, float) | ||
for price in commitment_downwards_deviation_price_array[i] | ||
): | ||
commitment_downwards_deviation_price_array[i] = [ | ||
initialize_series(price, start, end, resolution) | ||
for price in commitment_downwards_deviation_price_array[i] | ||
] | ||
for i in range(0, len(commitment_upwards_deviation_price_array)): | ||
if len(commitment_upwards_deviation_price_array[i]) != 0: | ||
if all( | ||
isinstance(price, float) | ||
for price in commitment_upwards_deviation_price_array[i] | ||
): | ||
commitment_upwards_deviation_price_array[i] = [ | ||
initialize_series(price, start, end, resolution) | ||
for price in commitment_upwards_deviation_price_array[i] | ||
] | ||
|
||
model = ConcreteModel() | ||
|
||
|
@@ -114,13 +123,14 @@ def device_scheduler( # noqa C901 | |
0, len(device_constraints[0].index.to_pydatetime()) - 1, doc="Set of datetimes" | ||
) | ||
model.c = RangeSet(0, len(commitment_quantities) - 1, doc="Set of commitments") | ||
|
||
# Add parameters | ||
def price_down_select(m, c, j): | ||
return commitment_downwards_deviation_price[c].iloc[j] | ||
|
||
def price_up_select(m, c, j): | ||
return commitment_upwards_deviation_price[c].iloc[j] | ||
def price_down_select(m, d, c, j): | ||
return commitment_downwards_deviation_price_array[d][c].iloc[j] | ||
|
||
def price_up_select(m, d, c, j): | ||
return commitment_upwards_deviation_price_array[d][c].iloc[j] | ||
|
||
def commitment_quantity_select(m, c, j): | ||
return commitment_quantities[c].iloc[j] | ||
|
@@ -191,8 +201,8 @@ def device_derivative_up_efficiency(m, d, j): | |
return 1 | ||
return eff | ||
|
||
model.up_price = Param(model.c, model.j, initialize=price_up_select) | ||
model.down_price = Param(model.c, model.j, initialize=price_down_select) | ||
model.up_price = Param(model.d, model.c, model.j, initialize=price_up_select) | ||
model.down_price = Param(model.d, model.c, model.j, initialize=price_down_select) | ||
model.commitment_quantity = Param( | ||
model.c, model.j, initialize=commitment_quantity_select | ||
) | ||
|
@@ -220,10 +230,10 @@ def device_derivative_up_efficiency(m, d, j): | |
) | ||
model.device_power_up = Var(model.d, model.j, domain=NonNegativeReals, initialize=0) | ||
model.commitment_downwards_deviation = Var( | ||
model.c, model.j, domain=NonPositiveReals, initialize=0 | ||
model.d, model.c, model.j, domain=NonPositiveReals, initialize=0 | ||
) | ||
model.commitment_upwards_deviation = Var( | ||
model.c, model.j, domain=NonNegativeReals, initialize=0 | ||
model.d, model.c, model.j, domain=NonNegativeReals, initialize=0 | ||
) | ||
|
||
# Add constraints as a tuple of (lower bound, value, upper bound) | ||
|
@@ -270,8 +280,8 @@ def ems_flow_commitment_equalities(m, j): | |
return ( | ||
0, | ||
sum(m.commitment_quantity[:, j]) | ||
+ sum(m.commitment_downwards_deviation[:, j]) | ||
+ sum(m.commitment_upwards_deviation[:, j]) | ||
+ sum(m.commitment_downwards_deviation[:, :, j]) | ||
+ sum(m.commitment_upwards_deviation[:, :, j]) | ||
- sum(m.ems_power[:, j]), | ||
0, | ||
) | ||
|
@@ -307,8 +317,15 @@ def cost_function(m): | |
costs = 0 | ||
for c in m.c: | ||
for j in m.j: | ||
costs += m.commitment_downwards_deviation[c, j] * m.down_price[c, j] | ||
costs += m.commitment_upwards_deviation[c, j] * m.up_price[c, j] | ||
for d in m.d: | ||
costs += ( | ||
m.commitment_downwards_deviation[d, c, j] | ||
* m.down_price[d, c, j] | ||
) | ||
costs += ( | ||
m.commitment_upwards_deviation[d, c, j] | ||
* m.up_price[d, c, j] | ||
) | ||
return costs | ||
|
||
model.costs = Objective(rule=cost_function, sense=minimize) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I would show an example of how to pass a dictionary in the command line.
Moreover, I'm not sure this type (
dict
) would work straightaway, given that:{"a" : 2}
,a=1
,"a"=1
,a:1
or'{"a":2}'
.Test command:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correctly said.I propose we do something like this?
Using this now I can use the format { "19" : 21}
Let me know if that works?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @victorgarcia98
I worked on this now it supports for the following type of input :
flexmeasures add schedule for-storage --sensor-id 1 --consumption-price-sensor-per-device '{"1": 2}' --start ${TOMORROW}T07:00+01:00 --duration PT12H --soc-at-start 50% --roundtrip-efficiency 90% --as-job
Let me know if that's fine?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also given the introduction to the new price sensor parameters, I believe the
consumption_price_sensor
should no longer be a necessary argument in the CLI and rather an option.It worked for me by omitting the function call toreplace_deprecated_argument
.Is that how it is supposed to be done?