Skip to content
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

expose storage and ems power capacities as flex-model fields #850

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
42 changes: 31 additions & 11 deletions flexmeasures/data/models/planning/storage.py
Expand Up @@ -86,7 +86,18 @@ def _prepare(self, skip_validation: bool = False) -> tuple:
)

# Check for required Sensor attributes
self.sensor.check_required_attributes([("capacity_in_mw", (float, int))])
storage_power_capacity_in_mw = self.flex_model.get(
"storage_power_capacity_in_mw",
self.sensor.get_attribute("capacity_in_mw", None),
)

if not (
isinstance(storage_power_capacity_in_mw, float)
or isinstance(storage_power_capacity_in_mw, int)
):
raise ValueError(
"Storage power capacity not defined in the sensor attributes or the flex-model"
)
victorgarcia98 marked this conversation as resolved.
Show resolved Hide resolved

# Check for known prices or price forecasts, trimming planning window accordingly
up_deviation_prices, (start, end) = get_prices(
Expand Down Expand Up @@ -158,15 +169,11 @@ def _prepare(self, skip_validation: bool = False) -> tuple:
if sensor.get_attribute("is_strictly_non_positive"):
device_constraints[0]["derivative min"] = 0
else:
device_constraints[0]["derivative min"] = (
sensor.get_attribute("capacity_in_mw") * -1
)
device_constraints[0]["derivative min"] = storage_power_capacity_in_mw * -1
if sensor.get_attribute("is_strictly_non_negative"):
device_constraints[0]["derivative max"] = 0
else:
device_constraints[0]["derivative max"] = sensor.get_attribute(
"capacity_in_mw"
)
device_constraints[0]["derivative max"] = storage_power_capacity_in_mw

# Apply round-trip efficiency evenly to charging and discharging
device_constraints[0]["derivative down efficiency"] = (
Expand Down Expand Up @@ -199,10 +206,23 @@ def _prepare(self, skip_validation: bool = False) -> tuple:
ems_constraints = initialize_df(
StorageScheduler.COLUMNS, start, end, resolution
)
ems_capacity = sensor.generic_asset.get_attribute("capacity_in_mw")
if ems_capacity is not None:
ems_constraints["derivative min"] = ems_capacity * -1
ems_constraints["derivative max"] = ems_capacity

ems_power_capacity_in_mw = self.flex_model.get(
"ems_power_capacity_in_mw",
self.sensor.generic_asset.get_attribute("capacity_in_mw", None),
)

if ems_power_capacity_in_mw is not None:
if not (
isinstance(ems_power_capacity_in_mw, float)
or isinstance(ems_power_capacity_in_mw, int)
):
raise ValueError(
"EMS power capacity not defined in the sensor attributes or the flex-model"
)
victorgarcia98 marked this conversation as resolved.
Show resolved Hide resolved

ems_constraints["derivative min"] = ems_power_capacity_in_mw * -1
ems_constraints["derivative max"] = ems_power_capacity_in_mw

return (
sensor,
Expand Down
7 changes: 7 additions & 0 deletions flexmeasures/data/schemas/scheduling/storage.py
Expand Up @@ -77,6 +77,13 @@ class StorageFlexModelSchema(Schema):
soc_min = fields.Float(validate=validate.Range(min=0), data_key="soc-min")
soc_max = fields.Float(data_key="soc-max")

storage_power_capacity_in_mw = QuantityField(
victorgarcia98 marked this conversation as resolved.
Show resolved Hide resolved
"MW", required=False, data_key="storage-power-capacity-in-mw"
)
ems_power_capacity_in_mw = QuantityField(
victorgarcia98 marked this conversation as resolved.
Show resolved Hide resolved
"MW", required=False, data_key="ems-power-capacity-in-mw"
)

soc_maxima = fields.List(fields.Nested(SOCValueSchema()), data_key="soc-maxima")
soc_minima = fields.List(
fields.Nested(SOCValueSchema(value_validator=validate.Range(min=0))),
Expand Down