diff --git a/assume/common/base.py b/assume/common/base.py index a5c4f378..e82496c7 100644 --- a/assume/common/base.py +++ b/assume/common/base.py @@ -783,8 +783,8 @@ class LearningConfig(TypedDict): :type noise_scale: int :param noise_dt: Determines how quickly the noise weakens over time. :type noise_dt: int - :param load_learned_path: The path to the learned model to load. - :type load_learned_path: str + :param trained_actors_path: The path to the learned model to load. + :type trained_actors_path: str """ observation_dimension: int @@ -805,4 +805,4 @@ class LearningConfig(TypedDict): noise_sigma: float noise_scale: int noise_dt: int - load_learned_path: str + trained_actors_path: str diff --git a/assume/common/forecasts.py b/assume/common/forecasts.py index 7865defd..708405cf 100644 --- a/assume/common/forecasts.py +++ b/assume/common/forecasts.py @@ -226,9 +226,9 @@ def calculate_marginal_cost(self, pp_series: pd.Series): fuel_cost = fuel_price / pp_series["efficiency"] emissions_cost = co2_price * emission_factor / pp_series["efficiency"] - variable_cost = pp_series["var_cost"] if "var_cost" in pp_series else 0.0 + fixed_cost = pp_series["fixed_cost"] if "fixed_cost" in pp_series else 0.0 - marginal_cost = fuel_cost + emissions_cost + variable_cost + marginal_cost = fuel_cost + emissions_cost + fixed_cost return marginal_cost diff --git a/assume/common/outputs.py b/assume/common/outputs.py index 8e46d8ec..3e5e21f0 100644 --- a/assume/common/outputs.py +++ b/assume/common/outputs.py @@ -290,12 +290,16 @@ def write_market_orders(self, market_orders, market_id): # check if market results list is empty and skip the funktion and raise a warning if not market_orders: return + market_orders = separate_orders(market_orders) df = pd.DataFrame.from_records(market_orders, index="start_time") + del df["only_hours"] del df["agent_id"] + df["simulation"] = self.simulation_id df["market_id"] = market_id + self.write_dfs["market_orders"].append(df) def write_units_definition(self, unit_info: dict): @@ -380,6 +384,8 @@ async def on_stop(self): dfs.append(df) + # remove all empty dataframes + dfs = [df for df in dfs if not df.empty] if not dfs: return @@ -402,13 +408,11 @@ async def on_stop(self): def get_sum_reward(self): query = text( - "select value from kpis where variable = 'sum_reward' and ident = '{self.simulation_id}'" + f"select reward FROM rl_params where simulation='{self.simulation_id}'" ) - try: - with self.db.begin() as db: - avg_reward = db.execute(query).fetchall()[0] - except Exception: - avg_reward = 0 + with self.db.begin() as db: + reward = db.execute(query).fetchall() + avg_reward = sum(r[0] for r in reward) / len(reward) return avg_reward diff --git a/assume/common/scenario_loader.py b/assume/common/scenario_loader.py index a92ac824..e30393ae 100644 --- a/assume/common/scenario_loader.py +++ b/assume/common/scenario_loader.py @@ -234,7 +234,7 @@ async def load_scenario_folder_async( perform_evaluation: bool = False, episode: int = 0, eval_episode: int = 0, - load_learned_path: str = "", + trained_actors_path: str = "", ): """Load a scenario from a given path. Raises: ValueError: If the scenario or study case is not found. @@ -304,12 +304,12 @@ async def load_scenario_folder_async( ) learning_config["evaluation_mode"] = perform_evaluation - if "load_learned_path" not in learning_config.keys(): - if load_learned_path: - learning_config["load_learned_path"] = load_learned_path + if "trained_actors_path" not in learning_config.keys(): + if trained_actors_path: + learning_config["trained_actors_path"] = trained_actors_path else: learning_config[ - "load_learned_path" + "trained_actors_path" ] = f"{inputs_path}/learned_strategies/{sim_id}" if learning_config.get("learning_mode", False): @@ -451,6 +451,13 @@ async def load_scenario_folder_async( forecaster=forecaster, ) + if ( + world.learning_mode + and world.learning_role is not None + and len(world.learning_role.rl_strats) == 0 + ): + raise ValueError("No RL units/strategies were provided!") + async def async_load_custom_units( world: World, @@ -523,9 +530,9 @@ def load_scenario_folder( study_case: str, perform_learning: bool = True, perform_evaluation: bool = False, - episode: int = 0, - eval_episode: int = 0, - load_learned_path="", + episode: int = 1, + eval_episode: int = 1, + trained_actors_path="", ): """ Load a scenario from a given path. @@ -549,7 +556,7 @@ def load_scenario_folder( perform_evaluation=perform_evaluation, episode=episode, eval_episode=eval_episode, - load_learned_path=load_learned_path, + trained_actors_path=trained_actors_path, ) ) @@ -574,7 +581,13 @@ def run_learning(world: World, inputs_path: str, scenario: str, study_case: str) actors_and_critics = None world.output_role.del_similar_runs() + validation_interval = min( + world.learning_role.training_episodes, + world.learning_config.get("validation_episodes_interval", 5), + ) + eval_episode = 1 + for episode in tqdm( range(1, world.learning_role.training_episodes + 1), desc="Training Episodes", @@ -601,17 +614,16 @@ def run_learning(world: World, inputs_path: str, scenario: str, study_case: str) world.learning_role.turn_off_initial_exploration() world.run() + actors_and_critics = world.learning_role.extract_actors_and_critics() - validation_interval = min( - world.learning_role.training_episodes, - world.learning_config.get("validation_episodes_interval", 5), - ) + if ( episode % validation_interval == 0 and episode > world.learning_role.episodes_collecting_initial_experience ): - old_path = world.learning_config["load_learned_path"] + old_path = world.learning_config["trained_actors_path"] new_path = f"{old_path}_eval" + # save validation params in validation path world.learning_role.save_params(directory=new_path) world.reset() @@ -625,14 +637,16 @@ def run_learning(world: World, inputs_path: str, scenario: str, study_case: str) perform_learning=False, perform_evaluation=True, eval_episode=eval_episode, - load_learned_path=new_path, + trained_actors_path=new_path, ) + world.run() + avg_reward = world.output_role.get_sum_reward() # check reward improvement in validation run - world.learning_config["load_learned_path"] = old_path - if best_reward < avg_reward: - # save new best params for simulation + world.learning_config["trained_actors_path"] = old_path + if avg_reward > best_reward: + # update best reward best_reward = avg_reward world.learning_role.save_params(directory=old_path) diff --git a/assume/common/units_operator.py b/assume/common/units_operator.py index 7a0d1ad8..b0bd8495 100644 --- a/assume/common/units_operator.py +++ b/assume/common/units_operator.py @@ -164,11 +164,15 @@ def handle_market_feedback(self, content: ClearingMessage, meta: dict[str, str]) :type meta: dict[str, str] """ logger.debug(f"{self.id} got market result: {content}") - orderbook: Orderbook = content["orderbook"] + accepted_orders: Orderbook = content["accepted_orders"] + rejected_orders: Orderbook = content["rejected_orders"] + orderbook = accepted_orders + rejected_orders + 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.set_unit_dispatch(orderbook, marketconfig) diff --git a/assume/markets/base_market.py b/assume/markets/base_market.py index af49a802..68c6a51b 100644 --- a/assume/markets/base_market.py +++ b/assume/markets/base_market.py @@ -344,34 +344,34 @@ async def clear_market(self, market_products: list[MarketProduct]): ( accepted_orderbook, rejected_orderbook, - # pending_orderbook, market_meta, ) = self.clear(self.all_orders, market_products) self.all_orders = [] for order in rejected_orderbook: order["accepted_volume"] = 0 - order["accepted_price"] = 0 + order["accepted_price"] = market_meta[0]["price"] self.open_auctions - set(market_products) - # self.all_orders = pending_orderbook accepted_orderbook.sort(key=itemgetter("agent_id")) rejected_orderbook.sort(key=itemgetter("agent_id")) - accepted_bids = { + + accepted_orders = { agent: list(bids) for agent, bids in groupby(accepted_orderbook, itemgetter("agent_id")) } - rejected_bids = { + rejected_orders = { agent: list(bids) for agent, bids in groupby(rejected_orderbook, itemgetter("agent_id")) } + for agent in self.registered_agents: addr, aid = agent meta = {"sender_addr": self.context.addr, "sender_id": self.context.aid} closing: ClearingMessage = { "context": "clearing", "market_id": self.marketconfig.name, - "orderbook": accepted_bids.get(agent, []), - "rejected": rejected_bids.get(agent, []), + "accepted_orders": accepted_orders.get(agent, []), + "rejected_orders": rejected_orders.get(agent, []), } await self.context.send_acl_message( closing, diff --git a/assume/markets/clearing_algorithms/simple.py b/assume/markets/clearing_algorithms/simple.py index ecc8d549..0d134ae3 100644 --- a/assume/markets/clearing_algorithms/simple.py +++ b/assume/markets/clearing_algorithms/simple.py @@ -60,7 +60,8 @@ def clear( meta = [] orderbook.sort(key=market_getter) for product, product_orders in groupby(orderbook, market_getter): - accepted_product_orders: Orderbook = [] + accepted_demand_orders: Orderbook = [] + accepted_supply_orders: Orderbook = [] product_orders = list(product_orders) if product not in market_products: rejected_orders.extend(product_orders) @@ -89,15 +90,18 @@ def clear( # now add the next demand order dem_vol += -demand_order["volume"] demand_order["accepted_volume"] = demand_order["volume"] - to_commit: Orderbook = [] - # and add supply until the demand order is matched while supply_orders and gen_vol < dem_vol: supply_order = supply_orders.pop(0) if supply_order["price"] <= demand_order["price"]: + added = supply_order["volume"] - supply_order.get( + "accepted_volume", 0 + ) + should_insert = not supply_order.get("accepted_volume") supply_order["accepted_volume"] = supply_order["volume"] - to_commit.append(supply_order) - gen_vol += supply_order["volume"] + if should_insert: + accepted_supply_orders.append(supply_order) + gen_vol += added else: rejected_orders.append(supply_order) # now we know which orders we need @@ -107,47 +111,39 @@ def clear( if diff < 0: # gen < dem - # generation is not enough - split last demand bid - split_demand_order = demand_order.copy() - split_demand_order["accepted_volume"] = diff + # generation is not enough - accept partially demand_order["accepted_volume"] = demand_order["volume"] - diff - rejected_orders.append(split_demand_order) elif diff > 0: - # generation left over - split last generation bid - supply_order = to_commit[-1] - split_supply_order = supply_order.copy() - split_supply_order["volume"] = diff + # generation left over - accept generation bid partially + supply_order = accepted_supply_orders[-1] supply_order["accepted_volume"] = supply_order["volume"] - diff + # changed supply_order is still part of to_commit and will be added # only volume-diff can be sold for current price gen_vol -= diff # add left over to supply_orders again - supply_orders.insert(0, split_supply_order) + supply_orders.insert(0, supply_order) + demand_order["accepted_volume"] = demand_order["volume"] else: - # diff == 0 perfect match demand_order["accepted_volume"] = demand_order["volume"] - accepted_product_orders.append(demand_order) - accepted_product_orders.extend(to_commit) + accepted_demand_orders.append(demand_order) for order in supply_orders: rejected_orders.append(order) # set clearing price - merit order - uniform pricing - accepted_supply_orders = [ - x for x in accepted_product_orders if x["accepted_volume"] > 0 - ] if accepted_supply_orders: - clear_price = max(map(itemgetter("price"), accepted_supply_orders)) + clear_price = float( + max(map(itemgetter("price"), accepted_supply_orders)) + ) else: clear_price = 0 + accepted_product_orders = accepted_demand_orders + accepted_supply_orders for order in accepted_product_orders: order["accepted_price"] = clear_price - accepted_demand_orders = [ - x for x in accepted_product_orders if x["accepted_volume"] < 0 - ] accepted_orders.extend(accepted_product_orders) meta.append( @@ -182,7 +178,8 @@ def clear( meta = [] orderbook.sort(key=market_getter) for product, product_orders in groupby(orderbook, market_getter): - accepted_product_orders: Orderbook = [] + accepted_demand_orders: Orderbook = [] + accepted_supply_orders: Orderbook = [] if product not in market_products: rejected_orders.extend(product_orders) # log.debug(f'found unwanted bids for {product} should be {market_products}') @@ -242,27 +239,23 @@ def clear( gen_vol -= diff supply_orders.insert(0, split_supply_order) + demand_order["accepted_volume"] = demand_order["volume"] else: # diff == 0 perfect match demand_order["accepted_volume"] = demand_order["volume"] - accepted_orders.append(demand_order) + accepted_demand_orders.append(demand_order) # pay as bid for supply_order in to_commit: supply_order["accepted_price"] = supply_order["price"] demand_order["accepted_price"] = supply_order["price"] - accepted_product_orders.extend(to_commit) + accepted_supply_orders.extend(to_commit) for order in supply_orders: rejected_orders.append(order) - accepted_supply_orders = [ - x for x in accepted_product_orders if x["accepted_volume"] > 0 - ] - accepted_demand_orders = [ - x for x in accepted_product_orders if x["accepted_volume"] < 0 - ] + accepted_product_orders = accepted_demand_orders + accepted_supply_orders accepted_orders.extend(accepted_product_orders) meta.append( diff --git a/assume/strategies/learning_strategies.py b/assume/strategies/learning_strategies.py index d1f52187..230c7519 100644 --- a/assume/strategies/learning_strategies.py +++ b/assume/strategies/learning_strategies.py @@ -69,8 +69,8 @@ def __init__(self, *args, **kwargs): dt=kwargs.get("noise_dt", 1.0), ) - elif Path(load_path=kwargs["load_learned_path"]).is_dir(): - self.load_actor_params(load_path=kwargs["load_learned_path"]) + elif Path(load_path=kwargs["trained_actors_path"]).is_dir(): + self.load_actor_params(load_path=kwargs["trained_actors_path"]) def calculate_bids( self, @@ -364,14 +364,14 @@ def calculate_reward( timestep=start, ) - # calculate profit as income - running_cost from this event duration = (end - start) / timedelta(hours=1) - order_profit = order["price"] * order["volume"] * duration + + # calculate profit as income - running_cost from this event + price_difference = order["accepted_price"] - marginal_cost + order_profit = price_difference * order["accepted_volume"] * duration # calculate opportunity cost # as the loss of income we have because we are not running at full power - price_difference = order["price"] - marginal_cost - order_opportunity_cost = ( price_difference * ( @@ -411,9 +411,9 @@ def calculate_reward( reward = float(profit - regret_scale * opportunity_cost) * scaling # store results in unit outputs which are written to database by unit operator - unit.outputs["profit"].loc[start:end_excl] += float(profit) + unit.outputs["profit"].loc[start:end_excl] += profit unit.outputs["reward"].loc[start:end_excl] = reward - unit.outputs["regret"].loc[start:end_excl] = float(opportunity_cost) + unit.outputs["regret"].loc[start:end_excl] = opportunity_cost def load_actor_params(self, load_path): """ diff --git a/assume/units/powerplant.py b/assume/units/powerplant.py index 11f35126..4cbbeecb 100644 --- a/assume/units/powerplant.py +++ b/assume/units/powerplant.py @@ -77,7 +77,6 @@ def __init__( min_power: float = 0.0, efficiency: float = 1.0, fixed_cost: float = 0.0, - variable_cost: float | pd.Series = 0.0, partial_load_eff: bool = False, fuel_type: str = "others", emission_factor: float = 0.0, @@ -126,7 +125,6 @@ def __init__( ) self.fixed_cost = fixed_cost - self.variable_cost = variable_cost self.hot_start_cost = hot_start_cost * max_power self.warm_start_cost = warm_start_cost * max_power self.cold_start_cost = cold_start_cost * max_power @@ -197,7 +195,7 @@ def calc_simple_marginal_cost( marginal_cost = ( fuel_price / self.efficiency + self.forecaster.get_price("co2") * self.emission_factor / self.efficiency - + self.variable_cost + + self.fixed_cost ) return marginal_cost @@ -256,16 +254,10 @@ def calc_marginal_cost_with_partial_eff( efficiency = self.efficiency - eta_loss co2_price = self.forecaster.get_price("co2").at[timestep] - variable_cost = ( - self.variable_cost - if isinstance(self.variable_cost, float) - else self.variable_cost[timestep] - ) - marginal_cost = ( fuel_price / efficiency + co2_price * self.emission_factor / efficiency - + variable_cost + + self.fixed_cost ) return marginal_cost diff --git a/docker_configs/dashboard-definitions/ASSUME.json b/docker_configs/dashboard-definitions/ASSUME.json index 573adaaf..d5e020ad 100644 --- a/docker_configs/dashboard-definitions/ASSUME.json +++ b/docker_configs/dashboard-definitions/ASSUME.json @@ -24,7 +24,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 2, + "id": 1, "links": [], "liveNow": false, "panels": [ @@ -217,6 +217,30 @@ "value": "EUR/MWh" } ] + }, + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "Demand volume" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] } ] }, @@ -382,7 +406,7 @@ "h": 9, "w": 12, "x": 0, - "y": 32 + "y": 44 }, "id": 19, "options": { @@ -553,13 +577,41 @@ }, "unit": "megwatt" }, - "overrides": [] + "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 + } + } + ] + } + ] }, "gridPos": { "h": 9, "w": 12, "x": 12, - "y": 32 + "y": 44 }, "id": 20, "options": { @@ -679,7 +731,7 @@ "h": 1, "w": 24, "x": 0, - "y": 41 + "y": 53 }, "id": 74, "panels": [], @@ -768,7 +820,7 @@ "h": 12, "w": 10, "x": 0, - "y": 42 + "y": 54 }, "id": 72, "options": { @@ -902,7 +954,7 @@ "h": 12, "w": 9, "x": 10, - "y": 42 + "y": 54 }, "id": 76, "options": { @@ -987,7 +1039,7 @@ "h": 12, "w": 5, "x": 19, - "y": 42 + "y": 54 }, "id": 7, "options": { @@ -1080,7 +1132,7 @@ "h": 3, "w": 24, "x": 0, - "y": 54 + "y": 66 }, "id": 22, "options": { @@ -1135,7 +1187,7 @@ "h": 1, "w": 24, "x": 0, - "y": 57 + "y": 69 }, "id": 41, "panels": [], @@ -1207,7 +1259,7 @@ "h": 9, "w": 18, "x": 0, - "y": 58 + "y": 70 }, "id": 24, "options": { @@ -1324,7 +1376,7 @@ "h": 9, "w": 6, "x": 18, - "y": 58 + "y": 70 }, "id": 26, "options": { @@ -1448,6 +1500,32 @@ "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 + } + } + ] } ] }, @@ -1455,7 +1533,7 @@ "h": 8, "w": 12, "x": 0, - "y": 67 + "y": 79 }, "id": 70, "options": { @@ -1689,6 +1767,30 @@ "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 + } + } + ] } ] }, @@ -1696,7 +1798,7 @@ "h": 8, "w": 12, "x": 12, - "y": 67 + "y": 79 }, "id": 78, "options": { @@ -1725,7 +1827,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 ($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 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": [ [ @@ -1880,7 +1982,7 @@ "h": 9, "w": 24, "x": 0, - "y": 75 + "y": 87 }, "id": 82, "options": { @@ -1977,7 +2079,7 @@ "h": 1, "w": 24, "x": 0, - "y": 84 + "y": 96 }, "id": 39, "panels": [], @@ -2046,7 +2148,7 @@ "h": 9, "w": 18, "x": 0, - "y": 85 + "y": 97 }, "id": 36, "options": { @@ -2162,7 +2264,7 @@ "h": 9, "w": 6, "x": 18, - "y": 85 + "y": 97 }, "id": 37, "options": { @@ -2293,7 +2395,7 @@ "h": 9, "w": 12, "x": 0, - "y": 94 + "y": 106 }, "id": 89, "options": { @@ -2501,7 +2603,7 @@ "h": 9, "w": 12, "x": 12, - "y": 94 + "y": 106 }, "id": 90, "options": { @@ -2686,7 +2788,7 @@ "h": 9, "w": 24, "x": 0, - "y": 103 + "y": 115 }, "id": 86, "options": { @@ -2782,7 +2884,7 @@ "h": 1, "w": 24, "x": 0, - "y": 112 + "y": 124 }, "id": 44, "panels": [], @@ -2834,8 +2936,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2851,7 +2952,7 @@ "h": 9, "w": 18, "x": 0, - "y": 113 + "y": 125 }, "id": 65, "options": { @@ -2951,8 +3052,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2967,7 +3067,7 @@ "h": 9, "w": 6, "x": 18, - "y": 113 + "y": 125 }, "id": 47, "options": { @@ -3067,8 +3167,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3084,7 +3183,7 @@ "h": 9, "w": 24, "x": 0, - "y": 122 + "y": 134 }, "id": 80, "options": { @@ -3204,8 +3303,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3234,7 +3332,7 @@ "h": 9, "w": 12, "x": 0, - "y": 131 + "y": 143 }, "id": 91, "options": { @@ -3397,8 +3495,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3442,7 +3539,7 @@ "h": 9, "w": 12, "x": 12, - "y": 131 + "y": 143 }, "id": 92, "options": { @@ -3594,8 +3691,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -3627,7 +3723,7 @@ "h": 9, "w": 24, "x": 0, - "y": 140 + "y": 152 }, "id": 84, "options": { @@ -3726,9 +3822,9 @@ "list": [ { "current": { - "selected": false, - "text": "example_01_rl_base", - "value": "example_01_rl_base" + "selected": true, + "text": "example_01a_base", + "value": "example_01a_base" }, "datasource": { "type": "postgres", @@ -3777,11 +3873,15 @@ "selected": true, "text": [ "Unit 1", - "Unit 3" + "Unit 2", + "Unit 3", + "Unit 4" ], "value": [ "Unit 1", - "Unit 3" + "Unit 2", + "Unit 3", + "Unit 4" ] }, "datasource": { @@ -3805,9 +3905,13 @@ }, { "current": { - "selected": false, - "text": "demand_EOM", - "value": "demand_EOM" + "selected": true, + "text": [ + "demand_EOM" + ], + "value": [ + "demand_EOM" + ] }, "datasource": { "type": "postgres", @@ -3830,10 +3934,13 @@ }, { "current": { - "isNone": true, - "selected": false, - "text": "None", - "value": "" + "selected": true, + "text": [ + "None" + ], + "value": [ + "" + ] }, "datasource": { "type": "postgres", @@ -3858,7 +3965,7 @@ }, "time": { "from": "2018-12-31T23:00:00.000Z", - "to": "2019-01-31T22:59:59.000Z" + "to": "2019-01-10T22:59:59.000Z" }, "timepicker": { "refresh_intervals": [ @@ -3872,6 +3979,6 @@ "timezone": "", "title": "ASSUME: Main overview", "uid": "mQ3Lvkr4k", - "version": 6, + "version": 3, "weekStart": "" -} +} \ No newline at end of file diff --git a/docker_configs/dashboard-definitions/ASSUME_Learning.json b/docker_configs/dashboard-definitions/ASSUME_Learning.json index 68cb429d..a33423a4 100644 --- a/docker_configs/dashboard-definitions/ASSUME_Learning.json +++ b/docker_configs/dashboard-definitions/ASSUME_Learning.json @@ -25,7 +25,7 @@ "editable": true, "fiscalYearStartMonth": 0, "graphTooltip": 0, - "id": 3, + "id": 2, "links": [], "liveNow": false, "panels": [ @@ -303,7 +303,7 @@ "group": [], "metricColumn": "simulation", "rawQuery": true, - "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n episode,\n reward\nFROM rl_params\nwhere simulation like '${simulation}' || '_%'\nand learning_mode is true", + "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n (episode::float),\n reward\nFROM rl_params\nwhere simulation like '${simulation}' || '_%'\nand learning_mode is true", "refId": "A", "select": [ [ @@ -495,7 +495,7 @@ "group": [], "metricColumn": "simulation", "rawQuery": true, - "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n episode,\n reward\nFROM rl_params\nwhere simulation like '${simulation}' || '_%'\nand evaluation_mode is true", + "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n (episode::float),\n reward\nFROM rl_params\nwhere simulation like '${simulation}' || '_%'\nand evaluation_mode is true", "refId": "A", "select": [ [ @@ -800,8 +800,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -913,8 +912,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1002,8 +1000,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1115,8 +1112,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1182,7 +1178,7 @@ "group": [], "metricColumn": "simulation", "rawQuery": true, - "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n episode,\n reward\nFROM rl_params\nwhere simulation like '${simulation}' || '_%' AND\n unit = '$rl_unit'", + "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n (episode::float),\n reward\nFROM rl_params\nwhere simulation like '${simulation}' || '_%' AND\n unit = '$rl_unit'", "refId": "A", "select": [ [ @@ -1323,8 +1319,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1390,7 +1385,7 @@ "group": [], "metricColumn": "simulation", "rawQuery": true, - "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n episode,\n profit\nFROM rl_params\nwhere simulation like '${simulation}' || '_%' AND\n unit = '$rl_unit'", + "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n (episode::float),\n profit\nFROM rl_params\nwhere simulation like '${simulation}' || '_%' AND\n unit = '$rl_unit'", "refId": "A", "select": [ [ @@ -1543,8 +1538,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1610,7 +1604,7 @@ "group": [], "metricColumn": "simulation", "rawQuery": true, - "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n episode,\n regret\nFROM rl_params\nwhere simulation like ${simulation} || '_%' AND\n unit = '$rl_unit'", + "rawSql": "SELECT\n datetime,\n simulation,\n unit,\n (episode::float),\n regret\nFROM rl_params\nwhere simulation like '${simulation}' || '_%' AND\n unit = '$rl_unit'", "refId": "A", "select": [ [ @@ -1770,8 +1764,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -1884,8 +1877,7 @@ "mode": "absolute", "steps": [ { - "color": "green", - "value": null + "color": "green" }, { "color": "red", @@ -2196,14 +2188,14 @@ "type": "postgres", "uid": "P7B13B9DF907EC40C" }, - "definition": "SELECT DISTINCT\nSUBSTRING(m.simulation, 0, LENGTH(m.simulation) +1 - strpos(REVERSE(m.simulation),'_')) AS market_simulation\nFROM rl_params m", + "definition": "SELECT DISTINCT\nSUBSTRING(m.simulation, 0, LENGTH(m.simulation) +1 - strpos(REVERSE(m.simulation),'_')) AS market_simulation\nFROM rl_params m\nwhere learning_mode = True", "description": "Can choose which simulation we want to show ", "hide": 0, "includeAll": false, "multi": false, "name": "simulation", "options": [], - "query": "SELECT DISTINCT\nSUBSTRING(m.simulation, 0, LENGTH(m.simulation) +1 - strpos(REVERSE(m.simulation),'_')) AS market_simulation\nFROM rl_params m", + "query": "SELECT DISTINCT\nSUBSTRING(m.simulation, 0, LENGTH(m.simulation) +1 - strpos(REVERSE(m.simulation),'_')) AS market_simulation\nFROM rl_params m\nwhere learning_mode = True", "refresh": 2, "regex": "", "skipUrlSync": false, @@ -2254,6 +2246,6 @@ "timezone": "", "title": "Assume: Training progress", "uid": "JKQzx0q4k", - "version": 7, + "version": 4, "weekStart": "" -} +} \ No newline at end of file diff --git a/examples/inputs/example_01_rl/config.yaml b/examples/inputs/example_01_rl/config.yaml index 107291d7..c23d5246 100644 --- a/examples/inputs/example_01_rl/config.yaml +++ b/examples/inputs/example_01_rl/config.yaml @@ -1,6 +1,6 @@ base: start_date: 2019-01-01 00:00 - end_date: 2019-01-10 00:00 + end_date: 2019-01-15 00:00 time_step: 1h save_frequency_hours: Null learning_mode: True @@ -12,12 +12,12 @@ base: load_model_path: None max_bid_price: 100 algorithm: matd3 - learning_rate: 0.001 + learning_rate: 0.0001 training_episodes: 30 - episodes_collecting_initial_experience: 2 + episodes_collecting_initial_experience: 5 train_freq: 24 gradient_steps: -1 - batch_size: 128 + batch_size: 64 gamma: 0.99 device: cpu noise_sigma: 0.1 diff --git a/examples/inputs/example_01_rl/fuel_prices_df.csv b/examples/inputs/example_01_rl/fuel_prices_df.csv index 6901c8eb..96ecfd4c 100644 --- a/examples/inputs/example_01_rl/fuel_prices_df.csv +++ b/examples/inputs/example_01_rl/fuel_prices_df.csv @@ -1,2 +1,2 @@ fuel,uranium,lignite,hard coal,natural gas,oil,biomass,co2 -price,1,2,10,25,40,20,25 +price,1,2,10,25,40,20,25 \ No newline at end of file diff --git a/examples/inputs/example_01_rl/powerplant_units.csv b/examples/inputs/example_01_rl/powerplant_units.csv index 35b318f8..d4573cb6 100644 --- a/examples/inputs/example_01_rl/powerplant_units.csv +++ b/examples/inputs/example_01_rl/powerplant_units.csv @@ -2,4 +2,4 @@ name,technology,bidding_energy,fuel_type,emission_factor,max_power,min_power,eff Unit 1,nuclear,naive,uranium,0.0,1000.0,200.0,0.3,10.3,Operator 1 Unit 2,lignite,naive,lignite,0.4,1000.0,200.0,0.5,1.65,Operator 1 Unit 3,hard coal,learning,hard coal,0.3,2000.0,200.0,0.4,5,Operator 1 -Unit 4,hard coal,naive,hard coal,0.3,2000,200.0,0.4,20,Operator 1 +Unit 4,hard coal,naive,hard coal,0.3,2000,200.0,0.4,20,Operator 1 \ No newline at end of file diff --git a/examples/inputs/example_01a/fuel_prices_df.csv b/examples/inputs/example_01a/fuel_prices_df.csv index 6901c8eb..96ecfd4c 100644 --- a/examples/inputs/example_01a/fuel_prices_df.csv +++ b/examples/inputs/example_01a/fuel_prices_df.csv @@ -1,2 +1,2 @@ fuel,uranium,lignite,hard coal,natural gas,oil,biomass,co2 -price,1,2,10,25,40,20,25 +price,1,2,10,25,40,20,25 \ No newline at end of file diff --git a/tests/test_flexable_strategies.py b/tests/test_flexable_strategies.py index dea124bf..94d7dcc5 100644 --- a/tests/test_flexable_strategies.py +++ b/tests/test_flexable_strategies.py @@ -25,7 +25,7 @@ def power_plant() -> PowerPlant: max_power=1000, min_power=200, efficiency=0.5, - variable_cost=10, + fixed_cost=10, bidding_strategies={}, fuel_type="lignite", emission_factor=0.5, diff --git a/tests/test_powerplant.py b/tests/test_powerplant.py index 6e3e7ca8..8e91585e 100644 --- a/tests/test_powerplant.py +++ b/tests/test_powerplant.py @@ -24,7 +24,7 @@ def power_plant_1() -> PowerPlant: max_power=1000, min_power=200, efficiency=0.5, - variable_cost=10, + fixed_cost=10, fuel_type="lignite", emission_factor=0.5, forecaster=ff, @@ -45,7 +45,7 @@ def power_plant_2() -> PowerPlant: max_power=1000, min_power=0, efficiency=0.5, - variable_cost=10, + fixed_cost=10, fuel_type="lignite", forecaster=ff, emission_factor=0.5, @@ -66,7 +66,7 @@ def power_plant_3() -> PowerPlant: max_power=1000, min_power=0, efficiency=0.5, - variable_cost=10, + fixed_cost=10, fuel_type="lignite", emission_factor=0.5, forecaster=ff, @@ -81,7 +81,7 @@ def test_init_function(power_plant_1, power_plant_2, power_plant_3): assert power_plant_1.max_power == 1000 assert power_plant_1.min_power == 200 assert power_plant_1.efficiency == 0.5 - assert power_plant_1.variable_cost == 10 + assert power_plant_1.fixed_cost == 10 assert power_plant_1.fuel_type == "lignite" assert power_plant_1.emission_factor == 0.5 assert power_plant_1.ramp_up == 1000 diff --git a/tests/test_simple_market_mechanisms.py b/tests/test_simple_market_mechanisms.py index d1f4448c..08167d0b 100644 --- a/tests/test_simple_market_mechanisms.py +++ b/tests/test_simple_market_mechanisms.py @@ -45,9 +45,6 @@ def test_market(): orderbook = extend_orderbook(products, 1000, 100, orderbook) orderbook = extend_orderbook(products, 900, 50, orderbook) - simple_dayahead_auction_config.market_mechanism = clearing_mechanisms[ - simple_dayahead_auction_config.market_mechanism - ] mr = PayAsClearRole(simple_dayahead_auction_config) accepted, rejected, meta = mr.clear(orderbook, products) assert meta[0]["demand_volume"] > 0 @@ -89,3 +86,29 @@ def test_simple_market_mechanism(): # print(meta) # return mr.all_orders, meta + + +def test_market_pay_as_clear(): + next_opening = simple_dayahead_auction_config.opening_hours.after(datetime.now()) + products = get_available_products( + simple_dayahead_auction_config.market_products, next_opening + ) + assert len(products) == 1 + + """ + Create Orderbook with constant order volumes and prices: + - dem1: volume = -1000, price = 3000 + - gen1: volume = 1000, price = 100 + - gen2: volume = 900, price = 50 + """ + orderbook = extend_orderbook(products, -400, 3000) + orderbook = extend_orderbook(products, -100, 3000, orderbook) + orderbook = extend_orderbook(products, 300, 100, orderbook) + orderbook = extend_orderbook(products, 200, 50, orderbook) + + mr = PayAsClearRole(simple_dayahead_auction_config) + accepted, rejected, meta = mr.clear(orderbook, products) + assert meta[0]["demand_volume"] > 0 + assert meta[0]["price"] > 0 + assert len(accepted) == 4 + assert len(rejected) == 0 diff --git a/tests/utils.py b/tests/utils.py index 2b674d8c..7e339282 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -43,7 +43,7 @@ def extend_orderbook( products, volume, price, - orderbook=[], + orderbook=None, bid_type="SB", min_acceptance_ratio=None, ): @@ -52,6 +52,8 @@ def extend_orderbook( with specified values for price and volume and appends the orderbook """ + if not orderbook: + orderbook = [] if volume == 0: return orderbook @@ -94,7 +96,7 @@ def extend_orderbook( "agent_id": agent_id, "bid_id": f"bid_{len(orderbook)+1}", "volume": volume, - "accepted_volume": None, + "accepted_volume": 0, "price": price, "accepted_price": None, "only_hours": None,