From c1147900fa01116d467b78b863984aea14cd9db2 Mon Sep 17 00:00:00 2001 From: Nick Harder Date: Tue, 10 Dec 2024 12:42:45 +0100 Subject: [PATCH 1/2] Adds pay as bid option to the complex clearing -add pricing mechanism as param_dict argument -move log_flows also to the param_dict -update the docstrings for better explanation -rename the market clearing to complex clearing -rename it in config files and add param_dict for demonstration -run pre-commit --- assume/common/forecasts.py | 2 - assume/common/grid_utils.py | 7 +- assume/common/outputs.py | 2 +- .../markets/clearing_algorithms/__init__.py | 2 +- .../clearing_algorithms/complex_clearing.py | 77 ++++++++++++------- .../markets/clearing_algorithms/redispatch.py | 8 +- assume/scenario/loader_amiris.py | 2 +- assume/scenario/loader_oeds.py | 4 +- assume/scenario/loader_pypsa.py | 17 ++-- .../ASSUME Comparison.json | 2 +- .../dashboard-definitions/ASSUME.json | 2 +- .../dashboard-definitions/ASSUME_nodal.json | 2 +- examples/inputs/example_01a/config.yaml | 5 +- examples/inputs/example_01c/config.yaml | 13 ++-- examples/inputs/example_01d/config.yaml | 9 ++- examples/inputs/example_02d/config.yaml | 7 +- examples/inputs/example_03/config.yaml | 8 +- .../notebooks/08_market_zone_coupling.ipynb | 4 +- .../notebooks/09_example_Sim_and_xRL.ipynb | 2 +- tests/test_clearing_paper_examples.py | 2 +- tests/test_complex_market_mechanisms.py | 2 +- 21 files changed, 116 insertions(+), 63 deletions(-) diff --git a/assume/common/forecasts.py b/assume/common/forecasts.py index 6ae1d7878..64de4706c 100644 --- a/assume/common/forecasts.py +++ b/assume/common/forecasts.py @@ -557,8 +557,6 @@ def __init__( for key, value in kwargs.items(): self.data_dict[key] = FastSeries(index=self.index, value=value, name=key) - - def __getitem__(self, column: str) -> FastSeries: """ Retrieves forecasted values. diff --git a/assume/common/grid_utils.py b/assume/common/grid_utils.py index 3ee6c5016..4e9a2bbca 100644 --- a/assume/common/grid_utils.py +++ b/assume/common/grid_utils.py @@ -49,7 +49,12 @@ def add_generators( ) else: # add generators - generators.drop(["p_min_pu", "p_max_pu", "marginal_cost"], axis=1, inplace=True, errors="ignore") + generators.drop( + ["p_min_pu", "p_max_pu", "marginal_cost"], + axis=1, + inplace=True, + errors="ignore", + ) network.add( "Generator", name=generators.index, diff --git a/assume/common/outputs.py b/assume/common/outputs.py index 8e683b98e..dd97331b7 100644 --- a/assume/common/outputs.py +++ b/assume/common/outputs.py @@ -687,7 +687,7 @@ def get_sum_reward(self): ) if self.db is None: return [] - + with self.db.begin() as db: rewards_by_unit = db.execute(query).fetchall() diff --git a/assume/markets/clearing_algorithms/__init__.py b/assume/markets/clearing_algorithms/__init__.py index 174fde10a..e7838ef20 100644 --- a/assume/markets/clearing_algorithms/__init__.py +++ b/assume/markets/clearing_algorithms/__init__.py @@ -14,7 +14,7 @@ "pay_as_clear": PayAsClearRole, "pay_as_bid": PayAsBidRole, "pay_as_bid_contract": PayAsBidContractRole, - "pay_as_clear_complex": ComplexClearingRole, + "complex_clearing": ComplexClearingRole, "pay_as_clear_complex_dmas": ComplexDmasClearingRole, } diff --git a/assume/markets/clearing_algorithms/complex_clearing.py b/assume/markets/clearing_algorithms/complex_clearing.py index ad938c624..3d61bf7ca 100644 --- a/assume/markets/clearing_algorithms/complex_clearing.py +++ b/assume/markets/clearing_algorithms/complex_clearing.py @@ -258,33 +258,43 @@ def energy_balance_rule(model, node, t): class ComplexClearingRole(MarketRole): """ - Defines the clearing algorithm for the complex market. + Defines an optimization-based market clearing algorithm with support for complex bid types, including block bids, linked bids, + minimum acceptance ratios, and profiled volumes. This class also supports network representations with either zonal or nodal + configurations, allowing the modeling of complex markets with multiple zones and power flow constraints. - The complex market is a pay-as-clear market with more complex bid structures, including minimum acceptance ratios, bid types, and profiled volumes. + The market clearing algorithm accepts the following additional arguments via the `param_dict` in the market configuration: - The class supports two types of network representations: + - `solver` (str): Specifies the solver to be used for the optimization problem. Default is 'appsi_highs'. + - `log_flows` (bool): Indicates whether to log the power flows on the lines. Default is False. + - `pricing_mechanism` (str): Defines the pricing mechanism to be used. Default is 'pay_as_clear', with an alternative option of 'pay_as_bid'. + - `zones_identifier` (str): The key in the bus data that identifies the zone each bus belongs to. Used for zonal representation. + + Example market configuration: + ``` + market_mechanism: complex_clearing + param_dict: + solver: apps_highs + log_flows: true + pricing_mechanism: pay_as_clear + zones_identifier: zone_id + ``` - 1. **Zonal Representation**: The network is divided into zones, and the incidence matrix represents the connections between these zones. + Network Representations: - 2. **Nodal Representation**: Each bus in the network is treated as a node, and the incidence matrix represents the connections between these nodes. + The class supports two types of network representations: - Zonal Representation: - If a `zones_identifier` is provided in the market configuration param_dict, the buses are grouped into zones based on this identifier. - The incidence matrix is then constructed to represent the power connections between these zones. The total transfer - capacity between zones is determined by the sum of the capacities of the lines connecting the zones. - - `zones_identifier` (str): The key in the bus data that identifies the zone to which each bus belongs. + 1. Zonal Representation: The network is divided into zones, and the incidence matrix represents the connections between these zones. + - If a `zones_identifier` is provided, buses are grouped into zones based on this identifier. The incidence matrix is then constructed to represent the power connections between these zones. The total transfer capacity between zones is determined by the sum of the capacities of the lines connecting the zones. - Nodal Representation: - If no `zones_identifier` is provided, each bus is treated as a separate node. - The incidence matrix is constructed to represent the power connections between these nodes. + 2. Nodal Representation: If no `zones_identifier` is provided, each bus is treated as a separate node, and the incidence matrix represents the connections between these nodes. Attributes: - marketconfig (MarketConfig): The market configuration. - incidence_matrix (pd.DataFrame): The incidence matrix representing the network connections. - nodes (list): List of nodes or zones in the network. + - `marketconfig` (MarketConfig): The market configuration. + - `incidence_matrix` (pd.DataFrame): The incidence matrix representing the power network connections. + - `nodes` (list): List of nodes or zones in the network, depending on the selected representation. Args: - marketconfig (MarketConfig): The market configuration. + - `marketconfig` (MarketConfig): The market configuration object containing all parameters for the market clearing process. """ required_fields = ["bid_type"] @@ -320,6 +330,11 @@ def __init__(self, marketconfig: MarketConfig): self.incidence_matrix = create_incidence_matrix(self.lines, buses) self.nodes = buses.index.values + self.log_flows = self.marketconfig.param_dict.get("log_flows", False) + self.pricing_mechanism = self.marketconfig.param_dict.get( + "pricing_mechanism", "pay_as_clear" + ) + def define_solver(self, solver: str): # Get the solver from the market configuration if solver == "highs": @@ -423,6 +438,7 @@ def clear( accepted_orders (Orderbook): The accepted orders. rejected_orders (Orderbook): The rejected orders. meta (list[dict]): The market clearing results. + flows (dict): The power flows on the lines. Notes: First the market clearing is solved using the cost minimization with the pyomo model market_clearing_opt. @@ -523,15 +539,14 @@ def clear( if all(order_surplus >= 0 for order_surplus in orders_surplus): break - log_flows = True - accepted_orders, rejected_orders, meta, flows = extract_results( model=instance, orders=orderbook, rejected_orders=rejected_orders, market_products=market_products, market_clearing_prices=market_clearing_prices, - log_flows=log_flows, + pricing_mechanism=self.pricing_mechanism, + log_flows=self.log_flows, ) self.all_orders = [] @@ -622,6 +637,7 @@ def extract_results( rejected_orders: Orderbook, market_products: list[MarketProduct], market_clearing_prices: dict, + pricing_mechanism: str = "pay_as_clear", log_flows: bool = False, ): """ @@ -638,6 +654,9 @@ def extract_results( tuple[Orderbook, Orderbook, list[dict]]: The accepted orders, rejected orders, and meta information """ + if pricing_mechanism not in ["pay_as_clear", "pay_as_bid"]: + raise ValueError(f"Invalid pricing mechanism {pricing_mechanism}") + accepted_orders: Orderbook = [] meta = [] @@ -651,9 +670,12 @@ def extract_results( # set the accepted volume and price for each simple bid order["accepted_volume"] = acceptance * order["volume"] - order["accepted_price"] = market_clearing_prices[order["node"]][ - order["start_time"] - ] + if pricing_mechanism == "pay_as_clear": + order["accepted_price"] = market_clearing_prices[order["node"]][ + order["start_time"] + ] + elif pricing_mechanism == "pay_as_bid": + order["accepted_price"] = order["price"] # calculate the total cleared supply and demand volume if order["accepted_volume"] > 0: @@ -672,9 +694,12 @@ def extract_results( # set the accepted volume and price for each block bid for start_time, volume in order["volume"].items(): order["accepted_volume"][start_time] = acceptance * volume - order["accepted_price"][start_time] = market_clearing_prices[ - order["node"] - ][start_time] + if pricing_mechanism == "pay_as_clear": + order["accepted_price"][start_time] = market_clearing_prices[ + order["node"] + ][start_time] + elif pricing_mechanism == "pay_as_bid": + order["accepted_price"][start_time] = order["price"] # calculate the total cleared supply and demand volume if order["accepted_volume"][start_time] > 0: diff --git a/assume/markets/clearing_algorithms/redispatch.py b/assume/markets/clearing_algorithms/redispatch.py index 3c00cb729..60f907c60 100644 --- a/assume/markets/clearing_algorithms/redispatch.py +++ b/assume/markets/clearing_algorithms/redispatch.py @@ -214,8 +214,12 @@ def clear( ) # return orderbook_df back to orderbook format as list of dicts - accepted_orders = orderbook_df[orderbook_df["accepted_volume"] != 0].to_dict("records") - rejected_orders = orderbook_df[orderbook_df["accepted_volume"] == 0].to_dict("records") + accepted_orders = orderbook_df[orderbook_df["accepted_volume"] != 0].to_dict( + "records" + ) + rejected_orders = orderbook_df[orderbook_df["accepted_volume"] == 0].to_dict( + "records" + ) meta = [] # calculate meta data such as total upwared and downward redispatch, total backup dispatch diff --git a/assume/scenario/loader_amiris.py b/assume/scenario/loader_amiris.py index cce19b2ae..0749ea905 100644 --- a/assume/scenario/loader_amiris.py +++ b/assume/scenario/loader_amiris.py @@ -255,7 +255,7 @@ def add_agent_to_world( "price": load["ValueOfLostLoad"], }, # demand_series might contain more values than index - NaiveForecast(index, demand=demand_series[:len(index)]), + NaiveForecast(index, demand=demand_series[: len(index)]), ) case "StorageTrader": diff --git a/assume/scenario/loader_oeds.py b/assume/scenario/loader_oeds.py index 1edeca7da..4ac3c6a80 100644 --- a/assume/scenario/loader_oeds.py +++ b/assume/scenario/loader_oeds.py @@ -215,12 +215,12 @@ def load_oeds( "postgresql://readonly:readonly@timescale.nowum.fh-aachen.de:5432/opendata", ) - default_nuts_config = 'DE1, DEA, DEB, DEC, DED, DEE, DEF' + default_nuts_config = "DE1, DEA, DEB, DEC, DED, DEE, DEF" nuts_config = os.getenv("NUTS_CONFIG", default_nuts_config).split(",") nuts_config = [n.strip() for n in nuts_config] year = 2019 start = datetime(year, 1, 1) - end = datetime(year, 1+1, 1) - timedelta(hours=1) + end = datetime(year, 1 + 1, 1) - timedelta(hours=1) marketdesign = [ MarketConfig( "EOM", diff --git a/assume/scenario/loader_pypsa.py b/assume/scenario/loader_pypsa.py index 600b4520c..a841b98e1 100644 --- a/assume/scenario/loader_pypsa.py +++ b/assume/scenario/loader_pypsa.py @@ -95,7 +95,7 @@ def load_pypsa( "bidding_strategies": bidding_strategies[unit_type][generator.name], "technology": "conventional", "node": generator.node, - "efficiency": 1, # do not use generator.efficiency as it is respected in marginal_cost, + "efficiency": 1, # do not use generator.efficiency as it is respected in marginal_cost, "fuel_type": generator.carrier, "ramp_up": ramp_up, "ramp_down": ramp_down, @@ -173,7 +173,7 @@ def load_pypsa( scenario = "world_pypsa" study_case = "ac_dc_meshed" # "pay_as_clear", "redispatch" or "nodal" - market_mechanism = "pay_as_clear_complex" + market_mechanism = "complex_clearing" match study_case: case "ac_dc_meshed": @@ -186,7 +186,7 @@ def load_pypsa( logger.info(f"invalid studycase: {study_case}") network = pd.DataFrame() - study_case = f"{study_case}_{market_mechanism}" + study_case = f"{study_case}_{market_mechanism}" start = network.snapshots[0] end = network.snapshots[-1] @@ -206,7 +206,12 @@ def load_pypsa( marketdesign.append( MarketConfig( "EOM", - rr.rrule(rr.HOURLY, interval=1, dtstart=start-timedelta(hours=0.5), until=end), + rr.rrule( + rr.HOURLY, + interval=1, + dtstart=start - timedelta(hours=0.5), + until=end, + ), timedelta(hours=0.25), "pay_as_clear", [MarketProduct(timedelta(hours=1), 1, timedelta(hours=1.5))], @@ -225,7 +230,9 @@ def load_pypsa( bidding_strategies = { "power_plant": defaultdict(lambda: default_strategies), - "demand": defaultdict(lambda: {mc.market_id: "naive_eom" for mc in marketdesign}), + "demand": defaultdict( + lambda: {mc.market_id: "naive_eom" for mc in marketdesign} + ), "storage": defaultdict(lambda: default_strategies), } diff --git a/docker_configs/dashboard-definitions/ASSUME Comparison.json b/docker_configs/dashboard-definitions/ASSUME Comparison.json index b165d5b33..0c425b40e 100644 --- a/docker_configs/dashboard-definitions/ASSUME Comparison.json +++ b/docker_configs/dashboard-definitions/ASSUME Comparison.json @@ -2396,4 +2396,4 @@ "uid": "vP8U8-q4k", "version": 5, "weekStart": "" -} \ No newline at end of file +} diff --git a/docker_configs/dashboard-definitions/ASSUME.json b/docker_configs/dashboard-definitions/ASSUME.json index a5ec97f01..d8e740947 100644 --- a/docker_configs/dashboard-definitions/ASSUME.json +++ b/docker_configs/dashboard-definitions/ASSUME.json @@ -4683,4 +4683,4 @@ "uid": "mQ3Lvkr4k", "version": 3, "weekStart": "" -} \ No newline at end of file +} diff --git a/docker_configs/dashboard-definitions/ASSUME_nodal.json b/docker_configs/dashboard-definitions/ASSUME_nodal.json index 06c3589e0..ad29cf9ce 100644 --- a/docker_configs/dashboard-definitions/ASSUME_nodal.json +++ b/docker_configs/dashboard-definitions/ASSUME_nodal.json @@ -1048,4 +1048,4 @@ "uid": "nodalview", "version": 21, "weekStart": "" -} \ No newline at end of file +} diff --git a/examples/inputs/example_01a/config.yaml b/examples/inputs/example_01a/config.yaml index 7e3a70df8..4462a054f 100644 --- a/examples/inputs/example_01a/config.yaml +++ b/examples/inputs/example_01a/config.yaml @@ -92,7 +92,10 @@ dam_with_complex_clearing: maximum_bid_price: 3000 minimum_bid_price: -500 price_unit: EUR/MWh - market_mechanism: pay_as_clear_complex + market_mechanism: complex_clearing + param_dict: + solver: highs + pricing_mechanism: pay_as_clear additional_fields: - bid_type - min_acceptance_ratio diff --git a/examples/inputs/example_01c/config.yaml b/examples/inputs/example_01c/config.yaml index a0880d582..0cf9b080a 100644 --- a/examples/inputs/example_01c/config.yaml +++ b/examples/inputs/example_01c/config.yaml @@ -25,9 +25,10 @@ eom_only: price_unit: EUR/MWh additional_fields: - bid_type - market_mechanism: pay_as_clear_complex + market_mechanism: complex_clearing param_dict: solver: highs + pricing_mechanism: pay_as_clear eom_and_crm: start_date: 2019-01-01 00:00 @@ -55,9 +56,10 @@ eom_and_crm: price_unit: EUR/MWh additional_fields: - bid_type - market_mechanism: pay_as_clear_complex + market_mechanism: complex_clearing param_dict: solver: highs + pricing_mechanism: pay_as_clear CRM_pos: operator: CRM_operator @@ -114,10 +116,11 @@ dam_with_complex_opt_clearing: maximum_bid_price: 3000 minimum_bid_price: -500 price_unit: EUR/MWh - market_mechanism: pay_as_clear_complex + market_mechanism: complex_clearing + param_dict: + solver: highs + pricing_mechanism: pay_as_clear additional_fields: - bid_type - min_acceptance_ratio - parent_bid_id - param_dict: - solver: highs diff --git a/examples/inputs/example_01d/config.yaml b/examples/inputs/example_01d/config.yaml index af1404356..4ab1218c7 100644 --- a/examples/inputs/example_01d/config.yaml +++ b/examples/inputs/example_01d/config.yaml @@ -73,11 +73,12 @@ zonal_case: maximum_bid_price: 3000 minimum_bid_price: -500 price_unit: EUR/MWh - market_mechanism: pay_as_clear_complex - additional_fields: - - bid_type - - node + market_mechanism: complex_clearing param_dict: network_path: . solver: highs zones_identifier: zone_id + pricing_mechanism: pay_as_clear + additional_fields: + - bid_type + - node diff --git a/examples/inputs/example_02d/config.yaml b/examples/inputs/example_02d/config.yaml index e52c39c78..fb74fa62a 100644 --- a/examples/inputs/example_02d/config.yaml +++ b/examples/inputs/example_02d/config.yaml @@ -46,13 +46,14 @@ dam: maximum_bid_price: 3000 minimum_bid_price: -500 price_unit: EUR/MWh - market_mechanism: pay_as_clear_complex + market_mechanism: complex_clearing + param_dict: + solver: highs + pricing_mechanism: pay_as_clear additional_fields: - bid_type - min_acceptance_ratio - parent_bid_id - param_dict: - solver: highs tiny: start_date: 2019-01-01 00:00 diff --git a/examples/inputs/example_03/config.yaml b/examples/inputs/example_03/config.yaml index d6b9b87ed..c2c33a2d3 100644 --- a/examples/inputs/example_03/config.yaml +++ b/examples/inputs/example_03/config.yaml @@ -50,7 +50,13 @@ dam_case_2019: maximum_bid_price: 3000 minimum_bid_price: -500 price_unit: EUR/MWh - market_mechanism: pay_as_clear + market_mechanism: complex_clearing + param_dict: + solver: highs + pricing_mechanism: pay_as_clear + additional_fields: + - bid_type + eom_crm_case_2019: start_date: 2019-01-01 00:00 diff --git a/examples/notebooks/08_market_zone_coupling.ipynb b/examples/notebooks/08_market_zone_coupling.ipynb index 608f187fe..f51f43d44 100644 --- a/examples/notebooks/08_market_zone_coupling.ipynb +++ b/examples/notebooks/08_market_zone_coupling.ipynb @@ -1517,7 +1517,7 @@ " \"maximum_bid_price\": 3000,\n", " \"minimum_bid_price\": -500,\n", " \"price_unit\": \"EUR/MWh\",\n", - " \"market_mechanism\": \"pay_as_clear_complex\",\n", + " \"market_mechanism\": \"complex_clearing\",\n", " \"additional_fields\": [\"bid_type\", \"node\"],\n", " \"param_dict\": {\"network_path\": \".\", \"zones_identifier\": \"zone_id\"},\n", " }\n", @@ -1567,7 +1567,7 @@ " - **maximum_bid_price:** Maximum price allowed per bid (`3000` EUR/MWh).\n", " - **minimum_bid_price:** Minimum price allowed per bid (`-500` EUR/MWh).\n", " - **price_unit:** Unit of price measurement (`EUR/MWh`).\n", - " - **market_mechanism:** The market clearing mechanism (`pay_as_clear_complex`).\n", + " - **market_mechanism:** The market clearing mechanism (`complex_clearing`).\n", " - **additional_fields:** Additional fields required for bids:\n", " - **bid_type:** Type of bid (e.g., supply or demand).\n", " - **node:** The market zone associated with the bid.\n", diff --git a/examples/notebooks/09_example_Sim_and_xRL.ipynb b/examples/notebooks/09_example_Sim_and_xRL.ipynb index 05c3e087b..a71500db2 100644 --- a/examples/notebooks/09_example_Sim_and_xRL.ipynb +++ b/examples/notebooks/09_example_Sim_and_xRL.ipynb @@ -412,7 +412,7 @@ " \"maximum_bid_price\": 3000,\n", " \"minimum_bid_price\": -500,\n", " \"price_unit\": \"EUR/MWh\",\n", - " \"market_mechanism\": \"pay_as_clear_complex\",\n", + " \"market_mechanism\": \"complex_clearing\",\n", " \"additional_fields\": [\"bid_type\", \"node\"],\n", " \"param_dict\": {\"network_path\": \".\", \"zones_identifier\": \"zone_id\"},\n", " }\n", diff --git a/tests/test_clearing_paper_examples.py b/tests/test_clearing_paper_examples.py index 0db54d975..9743edf0a 100644 --- a/tests/test_clearing_paper_examples.py +++ b/tests/test_clearing_paper_examples.py @@ -28,7 +28,7 @@ volume_unit="MW", volume_tick=0.1, price_unit="€/MW", - market_mechanism="pay_as_clear_complex", + market_mechanism="complex_clearing", ) eps = 1e-4 diff --git a/tests/test_complex_market_mechanisms.py b/tests/test_complex_market_mechanisms.py index fdd80db0c..ff0b14908 100644 --- a/tests/test_complex_market_mechanisms.py +++ b/tests/test_complex_market_mechanisms.py @@ -29,7 +29,7 @@ volume_tick=0.1, maximum_bid_volume=None, price_unit="€/MW", - market_mechanism="pay_as_clear_complex", + market_mechanism="complex_clearing", ) eps = 1e-4 From 472f14777c7bedf4e1194debf0635a40ff7d359a Mon Sep 17 00:00:00 2001 From: Nick Harder Date: Wed, 11 Dec 2024 10:17:46 +0100 Subject: [PATCH 2/2] -fix docstrings --- .../clearing_algorithms/complex_clearing.py | 72 ++++++++++--------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/assume/markets/clearing_algorithms/complex_clearing.py b/assume/markets/clearing_algorithms/complex_clearing.py index 3d61bf7ca..b8e895137 100644 --- a/assume/markets/clearing_algorithms/complex_clearing.py +++ b/assume/markets/clearing_algorithms/complex_clearing.py @@ -258,43 +258,51 @@ def energy_balance_rule(model, node, t): class ComplexClearingRole(MarketRole): """ - Defines an optimization-based market clearing algorithm with support for complex bid types, including block bids, linked bids, - minimum acceptance ratios, and profiled volumes. This class also supports network representations with either zonal or nodal - configurations, allowing the modeling of complex markets with multiple zones and power flow constraints. - - The market clearing algorithm accepts the following additional arguments via the `param_dict` in the market configuration: - - - `solver` (str): Specifies the solver to be used for the optimization problem. Default is 'appsi_highs'. - - `log_flows` (bool): Indicates whether to log the power flows on the lines. Default is False. - - `pricing_mechanism` (str): Defines the pricing mechanism to be used. Default is 'pay_as_clear', with an alternative option of 'pay_as_bid'. - - `zones_identifier` (str): The key in the bus data that identifies the zone each bus belongs to. Used for zonal representation. - - Example market configuration: - ``` - market_mechanism: complex_clearing - param_dict: - solver: apps_highs - log_flows: true - pricing_mechanism: pay_as_clear - zones_identifier: zone_id - ``` + This class defines an optimization-based market clearing algorithm with support for complex bid types, + including block bids, linked bids, minimum acceptance ratios, and profiled volumes. It supports network + representations with either zonal or nodal configurations, enabling the modeling of complex markets with + multiple zones and power flow constraints. - Network Representations: - - The class supports two types of network representations: - - 1. Zonal Representation: The network is divided into zones, and the incidence matrix represents the connections between these zones. - - If a `zones_identifier` is provided, buses are grouped into zones based on this identifier. The incidence matrix is then constructed to represent the power connections between these zones. The total transfer capacity between zones is determined by the sum of the capacities of the lines connecting the zones. + The market clearing algorithm accepts additional arguments via the `param_dict` in the market configuration. - 2. Nodal Representation: If no `zones_identifier` is provided, each bus is treated as a separate node, and the incidence matrix represents the connections between these nodes. + Args: + marketconfig (MarketConfig): The market configuration object containing all parameters for the market + clearing process. Attributes: - - `marketconfig` (MarketConfig): The market configuration. - - `incidence_matrix` (pd.DataFrame): The incidence matrix representing the power network connections. - - `nodes` (list): List of nodes or zones in the network, depending on the selected representation. + marketconfig (MarketConfig): The market configuration. + incidence_matrix (pd.DataFrame): The incidence matrix representing the power network connections. + nodes (list): List of nodes or zones in the network, depending on the selected representation. + + Supported Parameters in `param_dict`: + solver (str): Specifies the solver to be used for the optimization problem. Default is 'appsi_highs'. + log_flows (bool): Indicates whether to log the power flows on the lines. Default is False. + pricing_mechanism (str): Defines the pricing mechanism to be used. Default is 'pay_as_clear', with an + alternative option of 'pay_as_bid'. + zones_identifier (str): The key in the bus data that identifies the zone each bus belongs to. Used + for zonal representation. + + Example: + Example market configuration: + ``` + market_mechanism: complex_clearing + param_dict: + solver: apps_highs + log_flows: true + pricing_mechanism: pay_as_clear + zones_identifier: zone_id + ``` - Args: - - `marketconfig` (MarketConfig): The market configuration object containing all parameters for the market clearing process. + Network Representations: + - Zonal Representation: The network is divided into zones, and the incidence matrix represents + the connections between these zones. + - If a `zones_identifier` is provided, buses are grouped into zones based on this identifier. + The incidence matrix is constructed to represent the power connections between these zones. + The total transfer capacity between zones is determined by the sum of the capacities of the + lines connecting the zones. + + - Nodal Representation: If no `zones_identifier` is provided, each bus is treated as a separate + node, and the incidence matrix represents the connections between these nodes. """ required_fields = ["bid_type"]