Skip to content

Commit

Permalink
fix grid clearings usage
Browse files Browse the repository at this point in the history
  • Loading branch information
maurerle committed Nov 3, 2024
1 parent 37f4d1d commit 61420b1
Show file tree
Hide file tree
Showing 4 changed files with 447 additions and 44 deletions.
77 changes: 55 additions & 22 deletions emarketpy/clearing_algorithms/nodal_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@
read_pypsa_grid,
)
from ..market_objects import MarketConfig, Orderbook
from ..utils import suppress_output

log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)

logging.getLogger("linopy").setLevel(logging.WARNING)
logging.getLogger("pypsa").setLevel(logging.WARNING)
Expand Down Expand Up @@ -47,7 +48,10 @@ def __init__(self, marketconfig: MarketConfig):
self.network = pypsa.Network()
# set snapshots as list from the value marketconfig.producs.count converted to list
self.network.snapshots = range(marketconfig.market_products[0].count)
assert self.grid_data

if not self.grid_data:
logger.error(f"Market '{marketconfig.market_id}': grid_data is missing.")
raise ValueError("grid_data is missing.")

read_pypsa_grid(
network=self.network,
Expand All @@ -68,25 +72,23 @@ def __init__(self, marketconfig: MarketConfig):
loads=self.grid_data["loads"],
)

self.solver = marketconfig.param_dict.get("solver", "glpk")
self.env = None

self.solver = marketconfig.param_dict.get("solver", "highs")
if self.solver == "gurobi":
try:
from gurobipy import Env

self.env = Env()
self.env.setParam("LogToConsole", 0)
except ImportError:
log.error("gurobi not installed - using GLPK")
self.solver = "glpk"
self.solver_options = {"LogToConsole": 0, "OutputFlag": 0}
elif self.solver == "highs":
self.solver_options = {"output_flag": False, "log_to_console": False}

# set the market clearing principle
# as pay as bid or pay as clear
self.payment_mechanism = marketconfig.param_dict.get(
"payment_mechanism", "pay_as_bid"
)
assert self.payment_mechanism in ["pay_as_bid", "pay_as_clear"]

if self.payment_mechanism not in ["pay_as_bid", "pay_as_clear"]:
logger.error(
f"Market '{marketconfig.market_id}': Invalid payment mechanism '{self.payment_mechanism}'."
)
raise ValueError("Invalid payment mechanism.")

def setup(self):
super().setup()
Expand Down Expand Up @@ -149,17 +151,22 @@ def clear(
# Update marginal costs for generators
nodal_network.generators_t.marginal_cost.update(costs)

status, termination_condition = nodal_network.optimize(
solver_name=self.solver,
env=self.env,
)
with suppress_output():
status, termination_condition = nodal_network.optimize(
solver_name=self.solver,
solver_options=self.solver_options,
)

if status != "ok":
log.error(f"Solver exited with {termination_condition}")
logger.error(f"Solver exited with {termination_condition}")
raise Exception("Solver in redispatch market did not converge")

log_flows = True

# process dispatch data
self.process_dispatch_data(network=nodal_network, orderbook_df=orderbook_df)
flows = self.process_dispatch_data(
network=nodal_network, orderbook_df=orderbook_df, log_flows=log_flows
)

# return orderbook_df back to orderbook format as list of dicts
accepted_orders = orderbook_df.to_dict("records")
Expand All @@ -173,9 +180,14 @@ def clear(
calculate_network_meta(network=nodal_network, product=product, i=i)
)

return accepted_orders, rejected_orders, meta, {}
return accepted_orders, rejected_orders, meta, flows

def process_dispatch_data(self, network: pypsa.Network, orderbook_df: pd.DataFrame):
def process_dispatch_data(
self,
network: pypsa.Network,
orderbook_df: pd.DataFrame,
log_flows: bool = False,
):
"""
This function processes the dispatch data to calculate the dispatch volumes and prices
and update the orderbook with the accepted volumes and prices.
Expand Down Expand Up @@ -225,3 +237,24 @@ def process_dispatch_data(self, network: pypsa.Network, orderbook_df: pd.DataFra
nodal_marginal_prices[unit_node],
0,
)

# get flows from optimized pypsa network
if log_flows:
# extract flows
# write network flows here if applicable
flows = []

# Check if the model has the 'flows' attribute
if hasattr(network, "lines_t"):
flows = network.lines_t.p0

flows["datetime"] = orderbook_df["start_time"].unique()
# set datetime as index
flows = flows.set_index("datetime", drop=True)
# pivot the dataframe to have row per line column per datetime
flows = flows.stack().reset_index()

# rename columns
flows.columns = ["datetime", "line", "flow"]

return flows
50 changes: 28 additions & 22 deletions emarketpy/clearing_algorithms/redispatch.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
read_pypsa_grid,
)
from ..market_objects import MarketConfig, Orderbook
from ..utils import suppress_output

log = logging.getLogger(__name__)
logger = logging.getLogger(__name__)

logging.getLogger("linopy").setLevel(logging.WARNING)
logging.getLogger("pypsa").setLevel(logging.WARNING)
Expand Down Expand Up @@ -48,7 +49,10 @@ def __init__(self, marketconfig: MarketConfig):
self.network = pypsa.Network()
# set snapshots as list from the value marketconfig.producs.count converted to list
self.network.snapshots = range(marketconfig.market_products[0].count)
assert self.grid_data

if not self.grid_data:
logger.error(f"Market '{marketconfig.market_id}': grid_data is missing.")
raise ValueError("grid_data is missing.")

read_pypsa_grid(
network=self.network,
Expand All @@ -66,25 +70,23 @@ def __init__(self, marketconfig: MarketConfig):
loads=self.grid_data["loads"],
)

self.solver = marketconfig.param_dict.get("solver", "glpk")
self.env = None

self.solver = marketconfig.param_dict.get("solver", "highs")
if self.solver == "gurobi":
try:
from gurobipy import Env

self.env = Env()
self.env.setParam("LogToConsole", 0)
except ImportError:
log.error("gurobi not installed - using GLPK")
self.solver = "glpk"
self.solver_options = {"LogToConsole": 0, "OutputFlag": 0}
elif self.solver == "highs":
self.solver_options = {"output_flag": False, "log_to_console": False}

# set the market clearing principle
# as pay as bid or pay as clear
self.payment_mechanism = marketconfig.param_dict.get(
"payment_mechanism", "pay_as_bid"
)
assert self.payment_mechanism in ["pay_as_bid", "pay_as_clear"]

if self.payment_mechanism not in ["pay_as_bid", "pay_as_clear"]:
logger.error(
f"Market '{marketconfig.market_id}': Invalid payment mechanism '{self.payment_mechanism}'."
)
raise ValueError("Invalid payment mechanism.")

def setup(self):
super().setup()
Expand Down Expand Up @@ -183,15 +185,16 @@ def clear(

# if any line is congested, perform redispatch
if line_loading.max().max() > 1:
log.debug("Congestion detected")
logger.debug("Congestion detected")

status, termination_condition = redispatch_network.optimize(
solver_name=self.solver,
env=self.env,
)
with suppress_output():
status, termination_condition = redispatch_network.optimize(
solver_name=self.solver,
solver_options=self.solver_options,
)

if status != "ok":
log.error(f"Solver exited with {termination_condition}")
logger.error(f"Solver exited with {termination_condition}")
raise Exception("Solver in redispatch market did not converge")

# process dispatch data
Expand All @@ -201,7 +204,7 @@ def clear(

# if no congestion is detected set accepted volume and price to 0
else:
log.debug("No congestion detected")
logger.debug("No congestion detected")

# return orderbook_df back to orderbook format as list of dicts
accepted_orders = orderbook_df.to_dict("records")
Expand All @@ -215,7 +218,10 @@ def clear(
calculate_network_meta(network=redispatch_network, product=product, i=i)
)

return accepted_orders, rejected_orders, meta, {}
# write network flows here if applicable
flows = []

return accepted_orders, rejected_orders, meta, flows

def process_dispatch_data(self, network: pypsa.Network, orderbook_df: pd.DataFrame):
"""
Expand Down
Loading

0 comments on commit 61420b1

Please sign in to comment.