Skip to content

Commit

Permalink
Issue FlexMeasures#618 Step1-3 done
Browse files Browse the repository at this point in the history
  • Loading branch information
rajath-09 committed Apr 26, 2023
1 parent e82778e commit 0276764
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 38 deletions.
18 changes: 18 additions & 0 deletions flexmeasures/cli/data_add.py
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,20 @@ def create_schedule(ctx):
required=False,
help="Optimize production against this sensor. Defaults to the consumption price sensor. The sensor typically records an electricity price (e.g. in EUR/kWh), but this field can also be used to optimize against some emission intensity factor (e.g. in kg CO₂ eq./kWh). Follow up with the sensor's ID.",
)
@click.option(
"--consumption-price-sensor-per-device",
"consumption_price_sensor_per_device",
type=dict,
required=False,
# help="Optimize consumption against this sensor. The sensor typically records an electricity price (e.g. in EUR/kWh), but this field can also be used to optimize against some emission intensity factor (e.g. in kg CO₂ eq./kWh). Follow up with the sensor's ID.",
)
@click.option(
"--production-price-sensor-per-device",
"production_price_sensor_per_device",
type=dict,
required=False,
# help="Optimize production against this sensor. Defaults to the consumption price sensor. The sensor typically records an electricity price (e.g. in EUR/kWh), but this field can also be used to optimize against some emission intensity factor (e.g. in kg CO₂ eq./kWh). Follow up with the sensor's ID.",
)
@click.option(
"--optimization-context-id",
"optimization_context_sensor",
Expand Down Expand Up @@ -1023,6 +1037,8 @@ def add_schedule_for_storage(
production_price_sensor: Sensor,
optimization_context_sensor: Sensor,
inflexible_device_sensors: list[Sensor],
consumption_price_sensors_per_device: dict(Sensor, Sensor),
production_price_sensors_per_device: dict(Sensor, Sensor),
start: datetime,
duration: timedelta,
soc_at_start: ur.Quantity,
Expand Down Expand Up @@ -1103,6 +1119,8 @@ def add_schedule_for_storage(
"consumption-price-sensor": consumption_price_sensor.id,
"production-price-sensor": production_price_sensor.id,
"inflexible-device-sensors": [s.id for s in inflexible_device_sensors],
"consumption-price-sensors-per-device": {(power.id, price.id) for (power, price) in consumption_price_sensors_per_device.items()},
"production-price-sensors-per-device": {(power.id, price.id) for (power, price) in production_price_sensors_per_device.items()},
},
)
if as_job:
Expand Down
4 changes: 2 additions & 2 deletions flexmeasures/data/models/planning/linear_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ 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]],
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,
Expand Down
119 changes: 85 additions & 34 deletions flexmeasures/data/models/planning/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,56 +48,107 @@ def compute_schedule(
roundtrip_efficiency = self.flex_model.get("roundtrip_efficiency")
prefer_charging_sooner = self.flex_model.get("prefer_charging_sooner", True)

consumption_price_sensor = self.flex_context.get("consumption_price_sensor")
production_price_sensor = self.flex_context.get("production_price_sensor")
# consumption_price_sensor = self.flex_context.get("consumption_price_sensor")
# production_price_sensor = self.flex_context.get("production_price_sensor")
inflexible_device_sensors = self.flex_context.get(
"inflexible_device_sensors", []
)

consumption_price_sensor_per_device = self.flex_context.get("consumption_price_sensor_per_device", {})
production_price_sensor_per_device = self.flex_context.get("production_price_sensor_per_device", {})

# Check for required Sensor attributes
self.sensor.check_required_attributes([("capacity_in_mw", (float, int))])

# Check for known prices or price forecasts, trimming planning window accordingly
up_deviation_prices, (start, end) = get_prices(
(start, end),
resolution,
beliefs_before=belief_time,
price_sensor=consumption_price_sensor,
sensor=sensor,
allow_trimmed_query_window=False,
)
down_deviation_prices, (start, end) = get_prices(
(start, end),
resolution,
beliefs_before=belief_time,
price_sensor=production_price_sensor,
sensor=sensor,
allow_trimmed_query_window=False,
)
# up_deviation_prices, (start, end) = get_prices(
# (start, end),
# resolution,
# beliefs_before=belief_time,
# price_sensor=consumption_price_sensor,
# sensor=sensor,
# allow_trimmed_query_window=False,
# )
# down_deviation_prices, (start, end) = get_prices(
# (start, end),
# resolution,
# beliefs_before=belief_time,
# price_sensor=production_price_sensor,
# sensor=sensor,
# allow_trimmed_query_window=False,
# )

up_deviation_prices_array = []
for power_sensor, price_sensor in consumption_price_sensor_per_device.items():

# Check for known prices or price forecasts, trimming planning window accordingly
up_deviation_prices, (start, end) = get_prices(
(start, end),
resolution,
beliefs_before=belief_time,
price_sensor=price_sensor,
sensor=power_sensor,
allow_trimmed_query_window=False,
)
up_deviation_prices_array.append(up_deviation_prices)

down_deviation_prices_array = []
for power_sensor, price_sensor in production_price_sensor_per_device.items():

down_deviation_prices, (start, end) = get_prices(
(start, end),
resolution,
beliefs_before=belief_time,
price_sensor=price_sensor,
sensor=power_sensor,
allow_trimmed_query_window=False,
)
down_deviation_prices_array.append(down_deviation_prices)

start = pd.Timestamp(start).tz_convert("UTC")
end = pd.Timestamp(end).tz_convert("UTC")

# Add tiny price slope to prefer charging now rather than later, and discharging later rather than now.
# We penalise the future with at most 1 per thousand times the price spread.
# if prefer_charging_sooner:
# up_deviation_prices = add_tiny_price_slope(
# up_deviation_prices, "event_value"
# )
# down_deviation_prices = add_tiny_price_slope(
# down_deviation_prices, "event_value"
# )
if prefer_charging_sooner:
up_deviation_prices = add_tiny_price_slope(
up_deviation_prices, "event_value"
)
down_deviation_prices = add_tiny_price_slope(
down_deviation_prices, "event_value"
)

for i in range(0, len(up_deviation_prices_array)):
up_deviation_prices[i] = add_tiny_price_slope(
up_deviation_prices[i], "event_value"
)
for i in range(0, len(down_deviation_prices_array)):
down_deviation_prices[i] = add_tiny_price_slope(
down_deviation_prices[i], "event_value"
)
# Set up commitments to optimise for
commitment_quantities = [initialize_series(0, start, end, self.resolution)]

# Todo: convert to EUR/(deviation of commitment, which is in MW)
commitment_upwards_deviation_price = [
up_deviation_prices.loc[start : end - resolution]["event_value"]
]
commitment_downwards_deviation_price = [
down_deviation_prices.loc[start : end - resolution]["event_value"]
]
# commitment_upwards_deviation_price = [
# up_deviation_prices.loc[start : end - resolution]["event_value"]
# ]
# commitment_downwards_deviation_price = [
# down_deviation_prices.loc[start : end - resolution]["event_value"]
# ]

commitment_upwards_deviation_price_array = []
for up_deviation_price in up_deviation_prices_array:
commitment_upwards_deviation_price = [
up_deviation_price.loc[start : end - resolution]["event_value"]
]
commitment_upwards_deviation_price_array.append(commitment_upwards_deviation_price)

commitment_downwards_deviation_price_array = []
for down_deviation_price in down_deviation_prices_array:
commitment_downwards_deviation_price = [
down_deviation_price.loc[start : end - resolution]["event_value"]
]
commitment_downwards_deviation_price_array.append(commitment_downwards_deviation_price)

# Set up device constraints: only one scheduled flexible device for this EMS (at index 0), plus the forecasted inflexible devices (at indices 1 to n).
columns = [
Expand Down Expand Up @@ -169,8 +220,8 @@ def compute_schedule(
device_constraints,
ems_constraints,
commitment_quantities,
commitment_downwards_deviation_price,
commitment_upwards_deviation_price,
commitment_downwards_deviation_price_array,
commitment_upwards_deviation_price_array,
)
if scheduler_results.solver.termination_condition == "infeasible":
# Fallback policy if the problem was unsolvable
Expand Down
6 changes: 6 additions & 0 deletions flexmeasures/data/schemas/scheduling/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ class FlexContextSchema(Schema):
inflexible_device_sensors = fields.List(
SensorIdField(), data_key="inflexible-device-sensors"
)
consumption_price_sensors_per_device = fields.Dict(
SensorIdField(), SensorIdField(), data_key="consumption-price-sensors-per-device"
)
production_price_sensors_per_device = fields.Dict(
SensorIdField(), SensorIdField(), data_key="production-price-sensors-per-device"
)
4 changes: 2 additions & 2 deletions flyway/postgres/sql/V0001__.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1011,9 +1011,9 @@ INSERT INTO "public"."roles_users" ("id", "user_id", "role_id") VALUES (1, 1, 1)
INSERT INTO "public"."sensor" ("id", "name", "unit", "timezone", "event_resolution", "knowledge_horizon_fnc", "knowledge_horizon_par", "generic_asset_id", "attributes") VALUES (13, 'power-1', 'MW', 'Europe/Amsterdam', '00:15:00', 'ex_post', '{"ex_post_horizon": "P0D"}', 1, '{}');
INSERT INTO "public"."sensor" ("id", "name", "unit", "timezone", "event_resolution", "knowledge_horizon_fnc", "knowledge_horizon_par", "generic_asset_id", "attributes") VALUES (14, 'power-2', 'MW', 'Europe/Amsterdam', '00:15:00', 'ex_post', '{"ex_post_horizon": "P0D"}', 5, '{}');
INSERT INTO "public"."sensor" ("id", "name", "unit", "timezone", "event_resolution", "knowledge_horizon_fnc", "knowledge_horizon_par", "generic_asset_id", "attributes") VALUES (15, 'power-3', 'MW', 'Europe/Amsterdam', '00:15:00', 'ex_post', '{"ex_post_horizon": "P0D"}', 2, '{}');
INSERT INTO "public"."sensor" ("id", "name", "unit", "timezone", "event_resolution", "knowledge_horizon_fnc", "knowledge_horizon_par", "generic_asset_id", "attributes") VALUES (11, 'price-solar-1', 'EUR/MWh', 'Europe/Amsterdam', '01:00:00', 'x_days_ago_at_y_oclock', '{"x": 1, "y": 12, "z": "Europe/Paris"}',1, '{}');
INSERT INTO "public"."sensor" ("id", "name", "unit", "timezone", "event_resolution", "knowledge_horizon_fnc", "knowledge_horizon_par", "generic_asset_id", "attributes") VALUES (9, 'price-solar-1', 'EUR/MWh', 'Europe/Amsterdam', '01:00:00', 'x_days_ago_at_y_oclock', '{"x": 1, "y": 12, "z": "Europe/Paris"}',1, '{}');
INSERT INTO "public"."sensor" ("id", "name", "unit", "timezone", "event_resolution", "knowledge_horizon_fnc", "knowledge_horizon_par", "generic_asset_id", "attributes") VALUES (10, 'price-solar-2', 'EUR/MWh', 'Europe/Amsterdam', '01:00:00', 'x_days_ago_at_y_oclock', '{"x": 1, "y": 12, "z": "Europe/Paris"}', 5, '{}');
INSERT INTO "public"."sensor" ("id", "name", "unit", "timezone", "event_resolution", "knowledge_horizon_fnc", "knowledge_horizon_par", "generic_asset_id", "attributes") VALUES (9, 'battery-cprice', 'EUR/MWh', 'Europe/Amsterdam', '01:00:00', 'x_days_ago_at_y_oclock', '{"x": 1, "y": 12, "z": "Europe/Paris"}', 3, '{}');
INSERT INTO "public"."sensor" ("id", "name", "unit", "timezone", "event_resolution", "knowledge_horizon_fnc", "knowledge_horizon_par", "generic_asset_id", "attributes") VALUES (11, 'battery-cprice', 'EUR/MWh', 'Europe/Amsterdam', '01:00:00', 'x_days_ago_at_y_oclock', '{"x": 1, "y": 12, "z": "Europe/Paris"}', 3, '{}');


--
Expand Down

0 comments on commit 0276764

Please sign in to comment.