diff --git a/assume/common/market_objects.py b/assume/common/market_objects.py index 2d4f49bd..78f99cde 100644 --- a/assume/common/market_objects.py +++ b/assume/common/market_objects.py @@ -36,9 +36,13 @@ class Order(TypedDict): :param end_time: the end time of the order :type end_time: datetime :param volume: the volume of the order - :type volume: int + :type volume: float :param price: the price of the order - :type price: int + :type price: float + :param accepted_volume: the accepted volume of the order + :type accepted_volume: float + :param accepted_price: the accepted price of the order + :type accepted_price: float :param only_hours: tuple of hours from which this order is available, on multi day products :type only_hours: OnlyHours | None :param agent_id: the id of the agent @@ -187,18 +191,18 @@ class OpeningMessage(TypedDict): :type context: str :param market_id: the id of the market :type market_id: str - :param start: the start time of the market - :type start: float - :param stop: the stop time of the market - :type stop: float + :param start_time: the start time of the market + :type start_time: float + :param end_time: the stop time of the market + :type end_time: float :param products: list of products which are available at the market to be traded :type products: list[Product] """ context: str market_id: str - start: float - stop: float + start_time: float + end_time: float products: list[Product] @@ -210,13 +214,16 @@ class ClearingMessage(TypedDict): :type context: str :param market_id: the id of the market :type market_id: str - :param orderbook: the orderbook of the market - :type orderbook: Orderbook + :param accepted_orders: the orders accepted by the market + :type accepted_orders: Orderbook + :param rejected_orders: the orders rejected by the market + :type rejected_orders: Orderbook """ context: str market_id: str - orderbook: Orderbook + accepted_orders: Orderbook + rejected_orders: Orderbook class OrderBookMessage(TypedDict): @@ -237,6 +244,14 @@ class RegistrationReplyMessage(TypedDict): accepted: bool +class DataRequestMessage(TypedDict): + context: str + market_id: str + metric: str + start_time: datetime + end_time: datetime + + class MetaDict(TypedDict): """ Message Meta of a FIPA ACL Message diff --git a/assume/common/outputs.py b/assume/common/outputs.py index 1c09e454..d17d84d4 100644 --- a/assume/common/outputs.py +++ b/assume/common/outputs.py @@ -130,6 +130,7 @@ def setup(self): """ Sets up the WriteOutput instance by subscribing to messages and scheduling recurrent tasks of storing the data. """ + super().setup() self.context.subscribe_message( self, @@ -355,6 +356,7 @@ async def on_stop(self): """ This function makes it possible to calculate Key Performance Indicators """ + await super().on_stop() # insert left records into db await self.store_dfs() diff --git a/assume/common/units_operator.py b/assume/common/units_operator.py index dc3ee4b7..ed166206 100644 --- a/assume/common/units_operator.py +++ b/assume/common/units_operator.py @@ -3,6 +3,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later import logging +from collections import defaultdict from datetime import datetime from itertools import groupby from operator import itemgetter @@ -14,6 +15,7 @@ from assume.common.market_objects import ( ClearingMessage, + DataRequestMessage, MarketConfig, MetaDict, OpeningMessage, @@ -46,7 +48,6 @@ def __init__( ): super().__init__() - self.bids_map = {} self.available_markets = available_markets self.registered_markets: dict[str, MarketConfig] = {} self.last_sent_dispatch = 0 @@ -58,11 +59,12 @@ def __init__( self.use_portfolio_opt = opt_portfolio[0] self.portfolio_strategy = opt_portfolio[1] - # should be a list per product_type - self.valid_orders = [] + # valid_orders per product_type + self.valid_orders = defaultdict(list) self.units: dict[str, BaseUnit] = {} def setup(self): + super().setup() self.id = self.context.aid self.context.subscribe_message( self, @@ -82,6 +84,12 @@ def setup(self): lambda content, meta: content.get("context") == "registration", ) + self.context.subscribe_message( + self, + self.handle_data_request, + lambda content, meta: content.get("context") == "data_request", + ) + for market in self.available_markets: if self.participate(market): self.context.schedule_timestamp_task( @@ -149,6 +157,7 @@ async def register_market(self, market: MarketConfig): acl_metadata={ "sender_addr": self.context.addr, "sender_id": self.context.aid, + "reply_with": market.name, }, ), logger.debug(f"{self.id} sent market registration to {market.name}") @@ -164,9 +173,9 @@ def handle_opening(self, opening: OpeningMessage, meta: MetaDict): :type meta: MetaDict """ logger.debug( - f'{self.id} received opening from: {opening["market_id"]} {opening["start"]} until: {opening["stop"]}.' + f'{self.id} received opening from: {opening["market_id"]} {opening["start_time"]} until: {opening["end_time"]}.' ) - self.context.schedule_instant_task(coroutine=self.submit_bids(opening)) + self.context.schedule_instant_task(coroutine=self.submit_bids(opening, meta)) def handle_market_feedback(self, content: ClearingMessage, meta: MetaDict): """ @@ -187,14 +196,12 @@ def handle_market_feedback(self, content: ClearingMessage, meta: MetaDict): for order in orderbook: order["market_id"] = content["market_id"] - # map bid id to unit id - order["unit_id"] = self.bids_map[order["bid_id"]] - self.valid_orders.extend(orderbook) marketconfig = self.registered_markets[content["market_id"]] + self.valid_orders[marketconfig.product_type].extend(orderbook) self.set_unit_dispatch(orderbook, marketconfig) self.write_learning_params(orderbook, marketconfig) - self.write_actual_dispatch() + self.write_actual_dispatch(marketconfig.product_type) def handle_registration_feedback( self, content: RegistrationMessage, meta: MetaDict @@ -214,6 +221,31 @@ def handle_registration_feedback( else: logger.error("Market %s did not accept registration", meta["sender_id"]) + def handle_data_request(self, content: DataRequestMessage, meta: MetaDict): + unit = content["unit"] + metric_type = content["metric"] + start = content["start_time"] + end = content["end_time"] + + data = [] + try: + data = self.units[unit].outputs[metric_type][start:end] + except Exception: + logger.exception("error handling data request") + self.context.schedule_instant_acl_message( + content={ + "context": "data_response", + "data": data, + }, + receiver_addr=meta["sender_addr"], + receiver_id=meta["sender_id"], + acl_metadata={ + "sender_addr": self.context.addr, + "sender_id": self.context.aid, + "in_reply_to": meta.get("reply_with"), + }, + ) + def set_unit_dispatch(self, orderbook: Orderbook, marketconfig: MarketConfig): """ feeds the current market result back to the units @@ -233,7 +265,7 @@ def set_unit_dispatch(self, orderbook: Orderbook, marketconfig: MarketConfig): orderbook=orderbook, ) - def write_actual_dispatch(self): + def write_actual_dispatch(self, product_type: str): """ sends the actual aggregated dispatch curve works across multiple markets @@ -250,7 +282,10 @@ def write_actual_dispatch(self): start = datetime.utcfromtimestamp(last) market_dispatch = aggregate_step_amount( - self.valid_orders, start, now, groupby=["market_id", "unit_id"] + self.valid_orders[product_type], + start, + now, + groupby=["market_id", "unit_id"], ) unit_dispatch_dfs = [] for unit_id, unit in self.units.items(): @@ -269,8 +304,11 @@ def write_actual_dispatch(self): data["unit"] = unit_id unit_dispatch_dfs.append(data) - self.valid_orders = list( - filter(lambda x: x["end_time"] > now, self.valid_orders) + self.valid_orders[product_type] = list( + filter( + lambda x: x["end_time"] > now, + self.valid_orders[product_type], + ) ) db_aid = self.context.data_dict.get("output_agent_id") @@ -297,7 +335,7 @@ def write_actual_dispatch(self): }, ) - async def submit_bids(self, opening: OpeningMessage): + async def submit_bids(self, opening: OpeningMessage, meta: MetaDict): """ formulates an orderbook and sends it to the market. This will handle optional portfolio processing @@ -330,6 +368,7 @@ async def submit_bids(self, opening: OpeningMessage): "sender_id": self.context.aid, "sender_addr": self.context.addr, "conversation_id": "conversation01", + "in_reply_to": meta.get("reply_with"), } await self.context.send_acl_message( content={ @@ -404,8 +443,8 @@ async def formulate_bids( order["price"] = round(order["price"] / market.price_tick) order["bid_id"] = f"{unit_id}_{i+1}" + order["unit_id"] = unit_id orderbook.append(order) - self.bids_map[order["bid_id"]] = unit_id return orderbook diff --git a/assume/markets/base_market.py b/assume/markets/base_market.py index e3cea45a..bcba9d18 100644 --- a/assume/markets/base_market.py +++ b/assume/markets/base_market.py @@ -13,6 +13,7 @@ from assume.common.market_objects import ( ClearingMessage, + DataRequestMessage, MarketConfig, MarketProduct, MetaDict, @@ -41,9 +42,11 @@ class MarketMechanism: name: str def __init__(self, marketconfig: MarketConfig): + super().__init__() self.marketconfig = marketconfig self.open_auctions = set() self.all_orders = [] + self.results = [] def validate_registration( self, content: RegistrationMessage, meta: MetaDict @@ -163,6 +166,7 @@ def setup(self): Schedules the opening() method to run at the next opening time of the market. """ + super().setup() self.marketconfig.addr = self.context.addr self.marketconfig.aid = self.context.aid @@ -204,6 +208,15 @@ def accept_get_unmatched(content: dict, meta: MetaDict): and content.get("market_id") == self.marketconfig.name ) + def accept_data_request(content: dict, meta: MetaDict): + return ( + content.get("context") == "data_request" + and content.get("market_id") == self.marketconfig.name + ) + + self.context.subscribe_message( + self, self.handle_data_request, accept_data_request + ) self.context.subscribe_message(self, self.handle_orderbook, accept_orderbook) self.context.subscribe_message( self, self.handle_registration, accept_registration @@ -238,8 +251,8 @@ async def opening(self): opening_message: OpeningMessage = { "context": "opening", "market_id": self.marketconfig.name, - "start": market_open, - "stop": market_closing, + "start_time": market_open, + "end_time": market_closing, "products": products, } @@ -254,6 +267,7 @@ async def opening(self): acl_metadata={ "sender_addr": self.context.addr, "sender_id": self.context.aid, + "reply_with": f"{self.marketconfig.name}_{market_open}", }, ) @@ -306,6 +320,7 @@ def handle_registration(self, content: RegistrationMessage, meta: MetaDict): acl_metadata={ "sender_addr": self.context.addr, "sender_id": self.context.aid, + "in_reply_to": meta.get("reply_with"), }, ) @@ -337,10 +352,38 @@ def handle_orderbook(self, content: OrderBookMessage, meta: MetaDict): acl_metadata={ "sender_addr": self.context.addr, "sender_id": self.context.aid, - "in_reply_to": 1, + "in_reply_to": meta.get("reply_with"), }, ) + def handle_data_request(self, content: DataRequestMessage, meta: MetaDict): + metric_type = content["metric"] + start = content["start_time"] + end = content["end_time"] + + data = [] + try: + import pandas as pd + + data = pd.DataFrame(self.results) + data.index = data["time"] + data = data[metric_type][start:end] + except Exception: + logger.exception("error handling data request") + self.context.schedule_instant_acl_message( + content={ + "context": "data_response", + "data": data, + }, + receiver_addr=meta["sender_addr"], + receiver_id=meta["sender_id"], + acl_metadata={ + "sender_addr": self.context.addr, + "sender_id": self.context.aid, + "in_reply_to": meta.get("reply_with"), + }, + ) + def handle_get_unmatched(self, content: dict, meta: MetaDict): """ A handler which sends the orderbook with unmatched orders to an agent. @@ -442,6 +485,7 @@ async def clear_market(self, market_products: list[MarketProduct]): ) meta["market_id"] = self.marketconfig.name meta["time"] = meta["product_start"] + self.results.append(meta) await self.store_market_results(market_meta) diff --git a/docker_configs/dashboard-definitions/ASSUME.json b/docker_configs/dashboard-definitions/ASSUME.json index 6350a2ba..25912f19 100644 --- a/docker_configs/dashboard-definitions/ASSUME.json +++ b/docker_configs/dashboard-definitions/ASSUME.json @@ -225,7 +225,7 @@ "options": { "mode": "exclude", "names": [ - "Demand volume" + "Price" ], "prefix": "All except:", "readOnly": true @@ -406,7 +406,7 @@ "h": 9, "w": 12, "x": 0, - "y": 32 + "y": 116 }, "id": 19, "options": { @@ -447,7 +447,7 @@ ], "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\n $__timeGroupAlias(start_time,$__interval),\n avg(accepted_price::float) AS \" \",\n bid_id AS \"bid_id\"\nFROM market_orders\nWHERE\n $__timeFilter(start_time) AND\n market_id = '$market' AND\n simulation = '$simulation'\nGROUP BY 1, bid_id\nORDER BY 1", + "rawSql": "SELECT\n $__timeGroupAlias(start_time,$__interval),\n avg(accepted_price::float) AS \" \",\n bid_id AS \"bid_id\"\nFROM market_orders\nWHERE\n $__timeFilter(start_time) AND\n market_id = '$market' AND\n simulation = '$simulation'\nGROUP BY 1, unit_id, bid_id\nORDER BY 1", "refId": "A", "select": [ [ @@ -577,41 +577,13 @@ }, "unit": "megwatt" }, - "overrides": [ - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - " Unit 1_1", - " Unit 2_1", - " Unit 3_1", - " Unit 4_1" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] - } - ] + "overrides": [] }, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 32 + "y": 116 }, "id": 20, "options": { @@ -652,7 +624,7 @@ ], "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\n $__timeGroupAlias(start_time,$__interval),\n avg(accepted_volume) AS \" \",\n bid_id AS \"bid_id\"\nFROM market_orders\nWHERE\n $__timeFilter(start_time) AND\n market_id = '$market' AND\n simulation = '$simulation'\nGROUP BY 1, bid_id\nORDER BY 1", + "rawSql": "SELECT\n $__timeGroupAlias(start_time,$__interval),\n avg(accepted_volume) AS \" \",\n bid_id AS \"bid_id\"\nFROM market_orders\nWHERE\n $__timeFilter(start_time) AND\n market_id = '$market' AND\n simulation = '$simulation'\nGROUP BY 1, unit_id, bid_id\nORDER BY 1", "refId": "A", "select": [ [ @@ -731,7 +703,7 @@ "h": 1, "w": 24, "x": 0, - "y": 41 + "y": 125 }, "id": 74, "panels": [], @@ -820,7 +792,7 @@ "h": 12, "w": 10, "x": 0, - "y": 42 + "y": 126 }, "id": 72, "options": { @@ -954,7 +926,7 @@ "h": 12, "w": 9, "x": 10, - "y": 42 + "y": 126 }, "id": 76, "options": { @@ -1039,7 +1011,7 @@ "h": 12, "w": 5, "x": 19, - "y": 42 + "y": 126 }, "id": 7, "options": { @@ -1122,6 +1094,19 @@ "title": "Installed Generation Capacities", "type": "piechart" }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 138 + }, + "id": 41, + "panels": [], + "title": "Generation units $Gen_Units", + "type": "row" + }, { "datasource": { "type": "postgres", @@ -1132,7 +1117,7 @@ "h": 3, "w": 24, "x": 0, - "y": 54 + "y": 139 }, "id": 22, "options": { @@ -1181,19 +1166,6 @@ ], "type": "text" }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 57 - }, - "id": 41, - "panels": [], - "title": "Generation units $Gen_Units", - "type": "row" - }, { "datasource": { "type": "postgres", @@ -1259,7 +1231,7 @@ "h": 9, "w": 18, "x": 0, - "y": 58 + "y": 142 }, "id": 24, "options": { @@ -1376,7 +1348,7 @@ "h": 9, "w": 6, "x": 18, - "y": 58 + "y": 142 }, "id": 26, "options": { @@ -1463,7 +1435,7 @@ "type": "linear" }, "showPoints": "auto", - "spanNulls": false, + "spanNulls": true, "stacking": { "group": "A", "mode": "none" @@ -1500,32 +1472,6 @@ "value": "€/MW" } ] - }, - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "Bid price: {bid_id=\"Unit 4_1\", unit_id=\"Unit 4\"}", - "Bid price: {bid_id=\"Unit 3_2\", unit_id=\"Unit 3\"}", - "Bid price: {bid_id=\"Unit 3_1\", unit_id=\"Unit 3\"}" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] } ] }, @@ -1533,7 +1479,7 @@ "h": 8, "w": 12, "x": 0, - "y": 67 + "y": 151 }, "id": 70, "options": { @@ -1564,7 +1510,7 @@ "hide": false, "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n avg(accepted_price::float) AS \"Accepted price:\",\r\n concat(unit_id, ' - ', market_id) as \"unit_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Gen_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id\r\nORDER BY 1\r\n", + "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n avg(accepted_price::float) AS \"Accepted price:\",\r\n avg(price) AS \"Bid price:\",\r\n concat(unit_id, ' - ', market_id) as \"unit_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Gen_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id\r\nORDER BY 1\r\n", "refId": "B", "select": [ [ @@ -1608,6 +1554,7 @@ "type": "column" } ], + "hide": true, "metricColumn": "none", "rawQuery": true, "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n price AS \"Bid price:\",\r\n unit_id as \"unit_id\",\r\n bid_id as \"bid_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Gen_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id, price, bid_id\r\nORDER BY 1", @@ -1715,7 +1662,7 @@ "type": "linear" }, "showPoints": "auto", - "spanNulls": false, + "spanNulls": true, "stacking": { "group": "A", "mode": "none" @@ -1767,30 +1714,6 @@ "id": "unit" } ] - }, - { - "__systemRef": "hideSeriesFrom", - "matcher": { - "id": "byNames", - "options": { - "mode": "exclude", - "names": [ - "Accepted volume: {unit_id=\"Unit 3 - EOM\"}" - ], - "prefix": "All except:", - "readOnly": true - } - }, - "properties": [ - { - "id": "custom.hideFrom", - "value": { - "legend": false, - "tooltip": false, - "viz": true - } - } - ] } ] }, @@ -1798,7 +1721,7 @@ "h": 8, "w": 12, "x": 12, - "y": 67 + "y": 151 }, "id": 78, "options": { @@ -1827,7 +1750,7 @@ "group": [], "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n sum(accepted_volume) AS \"Accepted volume:\",\r\n concat(unit_id, ' - ', market_id) as \"unit_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Gen_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id\r\nORDER BY 1\r\n", + "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n sum(accepted_volume) AS \"Accepted volume:\",\r\n avg(volume) AS \"Bid volume:\",\r\n concat(unit_id, ' - ', market_id) as \"unit_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Gen_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id\r\nORDER BY 1\r\n", "refId": "A", "select": [ [ @@ -1857,7 +1780,7 @@ }, "format": "time_series", "group": [], - "hide": false, + "hide": true, "metricColumn": "none", "rawQuery": true, "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n volume AS \"Bid volume:\",\r\n unit_id as \"unit_id\",\r\n bid_id as \"bid_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Gen_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id, volume, bid_id\r\nORDER BY 1", @@ -1910,40 +1833,11 @@ "type": "postgres", "uid": "P7B13B9DF907EC40C" }, + "description": "", "fieldConfig": { "defaults": { "color": { - "mode": "palette-classic" - }, - "custom": { - "axisCenteredZero": false, - "axisColorMode": "text", - "axisLabel": "", - "axisPlacement": "auto", - "barAlignment": 0, - "drawStyle": "line", - "fillOpacity": 0, - "gradientMode": "none", - "hideFrom": { - "legend": false, - "tooltip": false, - "viz": false - }, - "lineInterpolation": "linear", - "lineWidth": 1, - "pointSize": 5, - "scaleDistribution": { - "type": "linear" - }, - "showPoints": "auto", - "spanNulls": false, - "stacking": { - "group": "A", - "mode": "none" - }, - "thresholdsStyle": { - "mode": "off" - } + "mode": "thresholds" }, "mappings": [], "thresholds": { @@ -1958,19 +1852,35 @@ "value": 80 } ] - } + }, + "unit": "currencyEUR" }, "overrides": [ { "matcher": { - "id": "byName", - "options": "energy_marginal_costs Unit 1" + "id": "byRegexp", + "options": "Profit .*" }, "properties": [ { "id": "color", "value": { - "fixedColor": "blue", + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "Production costs .*" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", "mode": "fixed" } } @@ -1982,25 +1892,22 @@ "h": 9, "w": 24, "x": 0, - "y": 75 + "y": 159 }, - "id": 82, + "id": 93, "options": { - "legend": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { "calcs": [ - "min", - "max", - "mean", "sum" ], - "displayMode": "table", - "placement": "bottom", - "showLegend": true + "fields": "", + "values": false }, - "tooltip": { - "mode": "single", - "sort": "none" - } + "textMode": "auto" }, "pluginVersion": "9.2.15", "targets": [ @@ -2013,7 +1920,7 @@ "group": [], "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_cashflow AS \"Cashflow\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Gen_Units)\r\nGROUP BY 1, unit, energy_cashflow\r\nORDER BY 1", + "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_cashflow AS \"Cashflow\",\r\n energy_marginal_costs AS \"Production costs\",\r\n energy_cashflow - energy_marginal_costs AS \"Profit\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Gen_Units)\r\nGROUP BY 1, unit, energy_cashflow, energy_marginal_costs\r\nORDER BY 1", "refId": "A", "select": [ [ @@ -2035,62 +1942,17 @@ "type": "macro" } ] - }, - { - "datasource": { - "type": "postgres", - "uid": "P7B13B9DF907EC40C" - }, - "format": "time_series", - "group": [], - "hide": false, - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_marginal_costs AS \"Production costs\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Gen_Units)\r\nGROUP BY 1, unit, energy_marginal_costs\r\nORDER BY 1", - "refId": "B", - "select": [ - [ - { - "params": [ - "power" - ], - "type": "column" - } - ] - ], - "table": "market_dispatch", - "timeColumn": "datetime", - "timeColumnType": "timestamp", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] } ], - "title": "Energy Related Cash Flow", - "type": "timeseries" - }, - { - "collapsed": false, - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 84 - }, - "id": 39, - "panels": [], - "title": "Demand units data $Demand_Units", - "type": "row" + "title": "Financial Overview ${__from:date:YYYY-MM-DD} until ${__to:date:YYYY-MM-DD}", + "type": "stat" }, { "datasource": { "type": "postgres", "uid": "P7B13B9DF907EC40C" }, + "description": "", "fieldConfig": { "defaults": { "color": { @@ -2110,14 +1972,14 @@ "tooltip": false, "viz": false }, - "lineInterpolation": "smooth", - "lineWidth": 2, + "lineInterpolation": "linear", + "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, - "showPoints": "always", - "spanNulls": true, + "showPoints": "auto", + "spanNulls": false, "stacking": { "group": "A", "mode": "none" @@ -2140,21 +2002,180 @@ } ] }, - "unit": "megwatt" + "unit": "currencyEUR" }, - "overrides": [] + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "Profit .*" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "Production costs .*" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + } + ] + } + ] }, "gridPos": { - "h": 9, - "w": 18, + "h": 5, + "w": 24, "x": 0, - "y": 85 + "y": 168 }, - "id": 36, + "id": 99, "options": { "legend": { - "calcs": [ - "min", + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.2.15", + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "format": "time_series", + "group": [], + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_cashflow AS \"Cashflow\",\r\n energy_marginal_costs AS \"Production costs\",\r\n energy_cashflow - energy_marginal_costs AS \"Profit\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Gen_Units)\r\nGROUP BY 1, unit, energy_cashflow, energy_marginal_costs\r\nORDER BY 1", + "refId": "A", + "select": [ + [ + { + "params": [ + "power" + ], + "type": "column" + } + ] + ], + "table": "market_dispatch", + "timeColumn": "datetime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Financial Overview ${__from:date:YYYY-MM-DD} until ${__to:date:YYYY-MM-DD}", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 173 + }, + "id": 39, + "panels": [], + "title": "Demand units data $Demand_Units", + "type": "row" + }, + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "smooth", + "lineWidth": 2, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "always", + "spanNulls": true, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "megwatt" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 18, + "x": 0, + "y": 174 + }, + "id": 36, + "options": { + "legend": { + "calcs": [ + "min", "max", "mean" ], @@ -2264,7 +2285,7 @@ "h": 9, "w": 6, "x": 18, - "y": 85 + "y": 174 }, "id": 37, "options": { @@ -2395,7 +2416,7 @@ "h": 9, "w": 12, "x": 0, - "y": 94 + "y": 183 }, "id": 89, "options": { @@ -2512,6 +2533,134 @@ "title": "Bid Prices", "type": "timeseries" }, + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "currencyEUR" + }, + "overrides": [ + { + "matcher": { + "id": "byName", + "options": "energy_marginal_costs demand_EOM" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "blue", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "Volume .*" + }, + "properties": [ + { + "id": "unit", + "value": "megwatt" + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "Price .*" + }, + "properties": [ + { + "id": "unit", + "value": "€/MW" + } + ] + } + ] + }, + "gridPos": { + "h": 5, + "w": 4, + "x": 12, + "y": 183 + }, + "id": 95, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.2.15", + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "format": "time_series", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n -energy_cashflow AS \"Cashflow\",\r\n unit\r\nFROM unit_dispatch d\r\nWHERE\r\n $__timeFilter(index) AND\r\n d.simulation = '$simulation' AND\r\n unit in ($Demand_Units)\r\nGROUP BY 1, unit, energy_cashflow, power\r\nORDER BY 1", + "refId": "A", + "select": [ + [ + { + "params": [ + "power" + ], + "type": "column" + } + ] + ], + "table": "market_dispatch", + "timeColumn": "datetime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Total Cost of Energy Dispatch", + "type": "stat" + }, { "datasource": { "type": "postgres", @@ -2600,10 +2749,10 @@ ] }, "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 94 + "h": 5, + "w": 8, + "x": 16, + "y": 183 }, "id": 90, "options": { @@ -2618,7 +2767,7 @@ "showLegend": true }, "tooltip": { - "mode": "single", + "mode": "multi", "sort": "none" } }, @@ -2632,7 +2781,113 @@ "group": [], "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n avg(accepted_volume) AS \"Accepted volume:\",\r\n concat(unit_id, ' - ', market_id) as \"unit_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Demand_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id\r\nORDER BY 1\r\n", + "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n -avg(accepted_volume) AS \"Accepted volume:\",\r\n -avg(volume) AS \"Bid volume:\",\r\n concat(unit_id, ' - ', market_id) as \"unit_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Demand_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id\r\nORDER BY 1\r\n", + "refId": "A", + "select": [ + [ + { + "params": [ + "power" + ], + "type": "column" + } + ] + ], + "table": "market_dispatch", + "timeColumn": "datetime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Bid volume", + "transformations": [ + { + "id": "calculateField", + "options": { + "alias": "volume_acceptance_ratio", + "binary": { + "left": "accepted_volume Unit 1", + "operator": "/", + "reducer": "sum", + "right": "bid_volume Unit 1" + }, + "mode": "binary", + "reduce": { + "reducer": "sum" + } + } + } + ], + "type": "timeseries" + }, + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "€/MW" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 4, + "x": 12, + "y": 188 + }, + "id": 96, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "mean" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.2.15", + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "format": "table", + "group": [], + "hide": false, + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\n sum(energy_cashflow)/sum(power) as \"Price per MW\",\n unit\nFROM unit_dispatch d\nWHERE\n $__timeFilter(index) AND\n d.simulation = '$simulation' AND\n unit in ($Demand_Units)\nGROUP BY unit\nORDER BY 1", "refId": "A", "select": [ [ @@ -2654,7 +2909,62 @@ "type": "macro" } ] + } + ], + "title": "weighted average Price in €/MW for Demand $Demand_Units", + "type": "stat" + }, + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "kwatth" + }, + "overrides": [] + }, + "gridPos": { + "h": 4, + "w": 8, + "x": 16, + "y": 188 + }, + "id": 94, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false }, + "textMode": "auto" + }, + "pluginVersion": "9.2.15", + "targets": [ { "datasource": { "type": "postgres", @@ -2665,8 +2975,8 @@ "hide": false, "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n avg(volume) AS \"Bid volume:\",\r\n unit_id\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Demand_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id\r\nORDER BY 1", - "refId": "B", + "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n -power*1e3 as \"Volume\",\r\n unit\r\nFROM unit_dispatch d\r\nWHERE\r\n $__timeFilter(index) AND\r\n d.simulation = '$simulation' AND\r\n unit in ($Demand_Units)\r\nGROUP BY 1, unit, energy_cashflow, power\r\nORDER BY 1", + "refId": "A", "select": [ [ { @@ -2689,26 +2999,8 @@ ] } ], - "title": "Bid volume", - "transformations": [ - { - "id": "calculateField", - "options": { - "alias": "volume_acceptance_ratio", - "binary": { - "left": "accepted_volume Unit 1", - "operator": "/", - "reducer": "sum", - "right": "bid_volume Unit 1" - }, - "mode": "binary", - "reduce": { - "reducer": "sum" - } - } - } - ], - "type": "timeseries" + "title": "Total dispatched Volume", + "type": "stat" }, { "datasource": { @@ -2788,7 +3080,7 @@ "h": 9, "w": 24, "x": 0, - "y": 103 + "y": 192 }, "id": 86, "options": { @@ -2804,7 +3096,7 @@ "showLegend": true }, "tooltip": { - "mode": "single", + "mode": "multi", "sort": "none" } }, @@ -2818,7 +3110,7 @@ "group": [], "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_cashflow AS \"Cashflow\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Demand_Units)\r\nGROUP BY 1, unit, energy_cashflow\r\nORDER BY 1", + "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n -energy_cashflow AS \"Cashflow\",\r\n -energy_marginal_costs AS \"Production costs\",\r\n energy_cashflow - energy_marginal_costs AS \"Profit\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Demand_Units)\r\nGROUP BY 1, unit, energy_cashflow, energy_marginal_costs\r\nORDER BY 1", "refId": "A", "select": [ [ @@ -2840,39 +3132,6 @@ "type": "macro" } ] - }, - { - "datasource": { - "type": "postgres", - "uid": "P7B13B9DF907EC40C" - }, - "format": "time_series", - "group": [], - "hide": false, - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_marginal_costs AS \"Production costs\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Demand_Units)\r\nGROUP BY 1, unit, energy_marginal_costs\r\nORDER BY 1", - "refId": "B", - "select": [ - [ - { - "params": [ - "power" - ], - "type": "column" - } - ] - ], - "table": "market_dispatch", - "timeColumn": "datetime", - "timeColumnType": "timestamp", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] } ], "title": "Energy Related Cashflow", @@ -2884,7 +3143,7 @@ "h": 1, "w": 24, "x": 0, - "y": 112 + "y": 201 }, "id": 44, "panels": [], @@ -2953,7 +3212,7 @@ "h": 9, "w": 18, "x": 0, - "y": 113 + "y": 202 }, "id": 65, "options": { @@ -3069,7 +3328,7 @@ "h": 9, "w": 6, "x": 18, - "y": 113 + "y": 202 }, "id": 47, "options": { @@ -3186,7 +3445,7 @@ "h": 9, "w": 24, "x": 0, - "y": 122 + "y": 211 }, "id": 80, "options": { @@ -3306,8 +3565,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3336,7 +3594,7 @@ "h": 9, "w": 12, "x": 0, - "y": 131 + "y": 220 }, "id": 91, "options": { @@ -3499,8 +3757,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3544,7 +3801,7 @@ "h": 9, "w": 12, "x": 12, - "y": 131 + "y": 220 }, "id": 92, "options": { @@ -3559,7 +3816,7 @@ "showLegend": true }, "tooltip": { - "mode": "single", + "mode": "multi", "sort": "none" } }, @@ -3573,7 +3830,7 @@ "group": [], "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n avg(accepted_volume) AS \"Accepted volume:\",\r\n concat(unit_id, ' - ', market_id) as \"unit_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Storage_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id\r\nORDER BY 1\r\n", + "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n avg(accepted_volume) AS \"Accepted volume:\",\r\n avg(volume) AS \"Bid volume:\",\r\n concat(unit_id, ' - ', market_id) as \"unit_id\"\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Storage_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id, market_id\r\nORDER BY 1\r\n", "refId": "A", "select": [ [ @@ -3603,7 +3860,7 @@ }, "format": "time_series", "group": [], - "hide": false, + "hide": true, "metricColumn": "none", "rawQuery": true, "rawSql": "SELECT\r\n $__timeGroupAlias(start_time,$__interval),\r\n avg(volume) AS \"Bid volume:\",\r\n unit_id\r\nFROM market_orders\r\nWHERE\r\n $__timeFilter(start_time) AND\r\n unit_id in ($Storage_Units) AND\r\n simulation = '$simulation'\r\nGROUP BY 1, unit_id\r\nORDER BY 1", @@ -3656,6 +3913,125 @@ "type": "postgres", "uid": "P7B13B9DF907EC40C" }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "currencyEUR" + }, + "overrides": [ + { + "matcher": { + "id": "byRegexp", + "options": "Profit .*" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "yellow", + "mode": "fixed" + } + } + ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "Production costs .*" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + } + ] + } + ] + }, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 229 + }, + "id": 97, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "sum" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.2.15", + "targets": [ + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "format": "time_series", + "group": [], + "metricColumn": "none", + "rawQuery": true, + "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_cashflow AS \"Cashflow\",\r\n energy_marginal_costs AS \"Production costs\",\r\n energy_cashflow - energy_marginal_costs AS \"Profit\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Storage_Units)\r\nGROUP BY 1, unit, energy_cashflow, energy_marginal_costs\r\nORDER BY 1", + "refId": "A", + "select": [ + [ + { + "params": [ + "power" + ], + "type": "column" + } + ] + ], + "table": "market_dispatch", + "timeColumn": "datetime", + "timeColumnType": "timestamp", + "where": [ + { + "name": "$__timeFilter", + "params": [], + "type": "macro" + } + ] + } + ], + "title": "Financial Overview ${__from:date:YYYY-MM-DD} until ${__to:date:YYYY-MM-DD}", + "type": "stat" + }, + { + "datasource": { + "type": "postgres", + "uid": "P7B13B9DF907EC40C" + }, + "description": "", "fieldConfig": { "defaults": { "color": { @@ -3696,8 +4072,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3710,18 +4085,57 @@ "overrides": [ { "matcher": { - "id": "byName", - "options": "energy_marginal_costs Storage 1" + "id": "byRegexp", + "options": "Profit .*" }, "properties": [ { "id": "color", "value": { - "fixedColor": "blue", + "fixedColor": "yellow", "mode": "fixed" } } ] + }, + { + "matcher": { + "id": "byRegexp", + "options": "Production costs .*" + }, + "properties": [ + { + "id": "color", + "value": { + "fixedColor": "purple", + "mode": "fixed" + } + } + ] + }, + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "Cashflow Goldisthal" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] } ] }, @@ -3729,26 +4143,22 @@ "h": 9, "w": 24, "x": 0, - "y": 140 + "y": 238 }, - "id": 84, + "id": 98, "options": { "legend": { - "calcs": [ - "min", - "max", - "mean", - "sum" - ], - "displayMode": "table", + "calcs": [], + "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { - "mode": "single", + "mode": "multi", "sort": "none" } }, + "pluginVersion": "9.2.15", "targets": [ { "datasource": { @@ -3759,7 +4169,7 @@ "group": [], "metricColumn": "none", "rawQuery": true, - "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_cashflow AS \"Cashflow\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Storage_Units)\r\nGROUP BY 1, unit, energy_cashflow\r\nORDER BY 1", + "rawSql": "SELECT\r\n $__timeGroupAlias(index,$__interval),\r\n energy_cashflow AS \"Cashflow\",\r\n energy_marginal_costs AS \"Production costs\",\r\n energy_cashflow - energy_marginal_costs AS \"Profit\",\r\n unit\r\nFROM unit_dispatch\r\nWHERE\r\n $__timeFilter(index) AND\r\n simulation = '$simulation' AND\r\n unit in ($Storage_Units)\r\nGROUP BY 1, unit, energy_cashflow, energy_marginal_costs\r\nORDER BY 1", "refId": "A", "select": [ [ @@ -3781,42 +4191,9 @@ "type": "macro" } ] - }, - { - "datasource": { - "type": "postgres", - "uid": "P7B13B9DF907EC40C" - }, - "format": "time_series", - "group": [], - "hide": false, - "metricColumn": "none", - "rawQuery": true, - "rawSql": "SELECT\n $__timeGroupAlias(index,$__interval),\nenergy_marginal_costs AS \"Production costs\",\nunit\nFROM unit_dispatch\nWHERE\n $__timeFilter(index) AND\n simulation = '$simulation' AND\n unit in ($Storage_Units)\nGROUP BY 1, unit, energy_marginal_costs\nORDER BY 1", - "refId": "B", - "select": [ - [ - { - "params": [ - "power" - ], - "type": "column" - } - ] - ], - "table": "market_dispatch", - "timeColumn": "datetime", - "timeColumnType": "timestamp", - "where": [ - { - "name": "$__timeFilter", - "params": [], - "type": "macro" - } - ] } ], - "title": "Energy Related Cash Flow", + "title": "Financial Overview ${__from:date:YYYY-MM-DD} until ${__to:date:YYYY-MM-DD}", "type": "timeseries" } ], @@ -3829,8 +4206,8 @@ { "current": { "selected": false, - "text": "example_01a_base", - "value": "example_01a_base" + "text": "example_02_dam_case_2019", + "value": "example_02_dam_case_2019" }, "datasource": { "type": "postgres", @@ -3878,16 +4255,18 @@ "current": { "selected": true, "text": [ - "Unit 1", - "Unit 2", - "Unit 3", - "Unit 4" + "Biomass", + "Hydro", + "Solar", + "Wind offshore", + "Wind onshore" ], "value": [ - "Unit 1", - "Unit 2", - "Unit 3", - "Unit 4" + "Biomass", + "Hydro", + "Solar", + "Wind offshore", + "Wind onshore" ] }, "datasource": { @@ -3942,10 +4321,10 @@ "current": { "selected": true, "text": [ - "None" + "Bleiloch" ], "value": [ - "" + "Bleiloch" ] }, "datasource": { @@ -3970,8 +4349,8 @@ ] }, "time": { - "from": "2018-12-31T23:00:00.000Z", - "to": "2019-01-10T22:59:59.000Z" + "from": "2019-01-05T04:24:23.217Z", + "to": "2019-01-05T14:38:48.092Z" }, "timepicker": { "refresh_intervals": [ @@ -3985,6 +4364,6 @@ "timezone": "", "title": "ASSUME: Main overview", "uid": "mQ3Lvkr4k", - "version": 2, + "version": 21, "weekStart": "" } diff --git a/examples/inputs/example_02/powerplant_units.csv b/examples/inputs/example_02/powerplant_units.csv index dff5e347..b7f1f6e0 100644 --- a/examples/inputs/example_02/powerplant_units.csv +++ b/examples/inputs/example_02/powerplant_units.csv @@ -1,9 +1,9 @@ name,technology,bidding_energy,bidding_capacity_pos,bidding_capacity_neg,fuel_type,emission_factor,max_power,min_power,efficiency,ramp_up,ramp_down,fixed_cost,hot_start_cost,warm_start_cost,cold_start_cost,min_operating_time,min_down_time,heat_extraction,max_heat_extraction,unit_operator -Wind onshore,wind_onshore,flexable_eom,-,-,renewable,0,53190,0,1,-1,-1,0,0,0,0,0,0,0,0,renewables_operator -Wind offshore,wind_offshore,flexable_eom,-,-,renewable,0,7560,0,1,-1,-1,0,0,0,0,0,0,0,0,renewables_operator -Solar,solar,flexable_eom,-,-,renewable,0,48860,0,1,-1,-1,0,0,0,0,0,0,0,0,renewables_operator -Hydro,hydro,flexable_eom,-,-,renewable,0,4940,0,1,-1,-1,0,0,0,0,0,0,0,0,renewables_operator -Biomass,biomass,flexable_eom,-,-,renewable,0,8340,0,1,-1,-1,0,0,0,0,0,0,0,0,renewables_operator +Wind onshore,wind_onshore,flexable_eom,-,-,renewable,0,53190,0,1,,,0,0,0,0,0,0,0,0,renewables_operator +Wind offshore,wind_offshore,flexable_eom,-,-,renewable,0,7560,0,1,,,0,0,0,0,0,0,0,0,renewables_operator +Solar,solar,flexable_eom,-,-,renewable,0,48860,0,1,,,0,0,0,0,0,0,0,0,renewables_operator +Hydro,hydro,flexable_eom,-,-,renewable,0,4940,0,1,,,0,0,0,0,0,0,0,0,renewables_operator +Biomass,biomass,flexable_eom,-,-,renewable,0,8340,0,1,,,0,0,0,0,0,0,0,0,renewables_operator KKW ISAR 2,nuclear,flexable_eom,flexable_pos_crm,flexable_neg_crm,uranium,0,1485,590,0.33,890,890,10.3,140,140,140,72,10,no,0,UNIPER KKW BROKDORF,nuclear,flexable_eom,flexable_pos_crm,flexable_neg_crm,uranium,0,1480,590,0.33,890,890,10.3,140,140,140,72,10,no,0,UNIPER KKW PHILIPPSBURG 2,nuclear,flexable_eom,flexable_pos_crm,flexable_neg_crm,uranium,0,1468,590,0.33,880,880,10.3,140,140,140,72,10,no,0,ENBW ENERGIE BADEN-WURTTEMBERG diff --git a/tests/test_data_request_mechanism.py b/tests/test_data_request_mechanism.py new file mode 100644 index 00000000..600b5207 --- /dev/null +++ b/tests/test_data_request_mechanism.py @@ -0,0 +1,132 @@ +# SPDX-FileCopyrightText: ASSUME Developers +# +# SPDX-License-Identifier: AGPL-3.0-or-later + +import asyncio +from datetime import datetime + +import pandas as pd +from dateutil import rrule as rr +from dateutil.relativedelta import relativedelta as rd +from mango import Agent, RoleAgent, create_container +from mango.util.clock import ExternalClock + +from assume.common.forecasts import NaiveForecast +from assume.common.market_objects import MarketConfig, MarketProduct +from assume.common.units_operator import UnitsOperator +from assume.markets.base_market import MarketRole +from assume.strategies.naive_strategies import NaiveStrategy +from assume.units.demand import Demand + +start = datetime(2020, 1, 1) +end = datetime(2020, 12, 2) + + +class DataRequester(Agent): + def __init__(self, container, suggested_aid): + super().__init__(container, suggested_aid) + self.await_message: asyncio.Future = None + + async def send_data_request( + self, receiver_addr, receiver_id, content: dict, reply_with + ): + self.await_message = asyncio.Future() + await self.send_acl_message( + content, + receiver_addr=receiver_addr, + receiver_id=receiver_id, + acl_metadata={ + "sender_addr": self.addr, + "sender_id": self.aid, + "reply_with": reply_with, + }, + ) + + return await self.await_message + + def handle_message(self, content, meta): + self.await_message.set_result((content, meta)) + + +async def test_request_messages(): + market_name = "Test" + marketconfig = MarketConfig( + market_name, + rr.rrule(rr.HOURLY, dtstart=start, until=end), + rd(hours=1), + "pay_as_clear", + [MarketProduct(rd(hours=1), 1, rd(hours=1))], + ) + clock = ExternalClock(0) + container = await create_container( + addr="world", connection_type="external_connection", clock=clock + ) + units_agent = RoleAgent(container, "test_operator") + units_role = UnitsOperator(available_markets=[marketconfig]) + units_agent.add_role(units_role) + + index = pd.date_range(start=start, end=end + pd.Timedelta(hours=4), freq="1h") + units_role.context.data_dict = {} + + params_dict = { + "bidding_strategies": {"energy": NaiveStrategy()}, + "technology": "energy", + "unit_operator": "test_operator", + "max_power": 1000, + "min_power": 0, + "forecaster": NaiveForecast(index, demand=1000), + } + unit = Demand("testdemand", index=index, **params_dict) + await units_role.add_unit(unit) + + market_role = MarketRole(marketconfig) + market_agent = RoleAgent(container, "market") + market_agent.add_role(market_role) + + dr = DataRequester(container, "data_requester") + + market_content = { + "context": "data_request", + "market_id": "Test", + "metric": "price", + "start_time": index[0], + "end_time": index[1], + } + unit_content = { + "context": "data_request", + "unit": "testdemand", + "metric": "energy", + "start_time": index[0], + "end_time": index[3], + } + + # market results are empty for now + content, meta = await dr.send_data_request( + "world", "market", market_content, "market_request" + ) + assert meta["in_reply_to"] == "market_request" + assert content["context"] == "data_response" + assert content["data"].empty + + market_role.results.append({"time": index[0], "price": 12}) + market_role.results.append({"time": index[1], "price": 18}) + content, meta = await dr.send_data_request( + "world", "market", market_content, "market_request" + ) + # price is now returned correctly + assert content["data"][index[0]] == 12 + + unit.outputs["energy"][index[1]] = 100 + unit.outputs["energy"][index[3]] = 200 + + content, meta = await dr.send_data_request( + "world", "test_operator", unit_content, "unit_request" + ) + assert meta["in_reply_to"] == "unit_request" + assert content["context"] == "data_response" + assert isinstance(content["data"], pd.Series) + assert content["data"][index[1]] == 100 + assert content["data"][index[2]] == 0 + assert content["data"][index[3]] == 200 + + await container.shutdown()