Market clearing and LMPs
Computing Locational Marginal Prices
Locational marginal prices (LMPs) refer to the cost of supplying electricity at a particular location of the netw0ork. Multiple methods for computing LMPs have been proposed in the literature. UnitCommitment.jl implements two commonly-used methods: conventional LMPs and Approximated Extended LMPs (AELMPs). To compute LMPs for a given unit commitment instance, the compute_lmp
function can be used, as shown in the examples below. The function accepts three arguments – a solved SCUC model, an LMP method, and a linear optimizer – and it returns a dictionary mapping (bus_name, time)
to the marginal price.
Most mixed-integer linear optimizers, such as HiGHS
, Gurobi
and CPLEX
can be used with compute_lmp
, with the notable exception of Cbc
, which does not support dual value evaluations. If using Cbc
, please provide Clp
as the linear optimizer.
Conventional LMPs
LMPs are conventionally computed by: (1) solving the SCUC model, (2) fixing all binary variables to their optimal values, and (3) re-solving the resulting linear programming model. In this approach, the LMPs are defined as the dual variables' values associated with the net injection constraints. The example below shows how to compute conventional LMPs for a given unit commitment instance. First, we build and optimize the SCUC model. Then, we call the compute_lmp
function, providing as the second argument ConventionalLMP()
.
using UnitCommitment
using HiGHS
import UnitCommitment: ConventionalLMP
# Read benchmark instance
instance = UnitCommitment.read_benchmark("matpower/case118/2018-01-01")
# Build the model
model = UnitCommitment.build_model(
instance = instance,
optimizer = HiGHS.Optimizer,
)
# Optimize the model
UnitCommitment.optimize!(model)
# Compute the LMPs using the conventional method
lmp = UnitCommitment.compute_lmp(
model,
ConventionalLMP(),
optimizer = HiGHS.Optimizer,
)
# Access the LMPs
# Example: "s1" is the scenario name, "b1" is the bus name, 1 is the first time slot
@show lmp["s1","b1", 1]
Approximate Extended LMPs
Approximate Extended LMPs (AELMPs) are an alternative method to calculate locational marginal prices which attemps to minimize uplift payments. The method internally works by modifying the instance data in three ways: (1) it sets the minimum power output of each generator to zero, (2) it averages the start-up cost over the offer blocks for each generator, and (3) it relaxes all integrality constraints. To compute AELMPs, as shown in the example below, we call compute_lmp
and provide AELMP()
as the second argument.
This method has two configurable parameters: allow_offline_participation
and consider_startup_costs
. If allow_offline_participation = true
, then offline generators are allowed to participate in the pricing. If instead allow_offline_participation = false
, offline generators are not allowed and therefore are excluded from the system. A solved UC model is optional if offline participation is allowed, but is required if not allowed. The method forces offline participation to be allowed if the UC model supplied by the user is not solved. For the second field, If consider_startup_costs = true
, then start-up costs are integrated and averaged over each unit production; otherwise the production costs stay the same. By default, both fields are set to true
.
This approximation method is still under active research, and has several limitations. The implementation provided in the package is based on MISO Phase I only. It only supports fast start resources. More specifically, the minimum up/down time of all generators must be 1, the initial power of all generators must be 0, and the initial status of all generators must be negative. The method does not support time-varying start-up costs. The method does not support multiple scenarios. If offline participation is not allowed, AELMPs treats an asset to be offline if it is never on throughout all time periods.
using UnitCommitment
using HiGHS
import UnitCommitment: AELMP
# Read benchmark instance
instance = UnitCommitment.read_benchmark("matpower/case118/2017-02-01")
# Build the model
model = UnitCommitment.build_model(
instance = instance,
optimizer = HiGHS.Optimizer,
)
# Optimize the model
UnitCommitment.optimize!(model)
# Compute the AELMPs
aelmp = UnitCommitment.compute_lmp(
model,
AELMP(
allow_offline_participation = false,
consider_startup_costs = true
),
optimizer = HiGHS.Optimizer
)
# Access the AELMPs
# Example: "s1" is the scenario name, "b1" is the bus name, 1 is the first time slot
# Note: although scenario is supported, the query still keeps the scenario keys for consistency.
@show aelmp["s1", "b1", 1]