updated LMP for UC scenario

pull/26/head
Jun He 3 years ago
parent 30a4284119
commit 2a6c206e08

@ -177,8 +177,8 @@ lmp = UnitCommitment.compute_lmp(
)
# Access the LMPs
# Example: "b1" is the bus name, 1 is the first time slot
@show lmp["b1", 1]
# 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
@ -220,6 +220,7 @@ aelmp = UnitCommitment.compute_lmp(
)
# Access the AELMPs
# Example: "b1" is the bus name, 1 is the first time slot
@show aelmp["b1", 1]
# 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]
```

@ -26,6 +26,7 @@ WARNING: This approximation method is not fully developed. The implementation is
1. It only supports Fast Start resources. More specifically, the minimum up/down time has to be zero.
2. The method does NOT support time-varying start-up costs.
3. An asset is considered offline if it is never on throughout all time periods.
4. The method does NOT support multiple scenarios.
Arguments
---------
@ -71,19 +72,20 @@ aelmp = UnitCommitment.compute_lmp(
)
# Access the AELMPs
# Example: "b1" is the bus name, 1 is the first time slot
@show aelmp["b1", 1]
# 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]
```
"""
function compute_lmp(
model::JuMP.Model,
method::AELMP;
optimizer,
)::OrderedDict{Tuple{String,Int},Float64}
)::OrderedDict{Tuple{String,String,Int},Float64}
@info "Building the approximation model..."
instance = deepcopy(model[:instance])
_aelmp_check_parameters(instance, model, method)
_modify_instance!(instance, model, method)
_modify_scenario!(instance.scenarios[1], model, method)
# prepare the result dictionary and solve the model
elmp = OrderedDict()
@ -116,6 +118,13 @@ function _aelmp_check_parameters(
model::JuMP.Model,
method::AELMP,
)
# CHECK: model cannot have multiple scenarios
if length(instance.scenarios) > 1
error(
"The method does NOT support multiple scenarios.",
)
end
sc = instance.scenarios[1]
# CHECK: model must be solved if allow_offline_participation=false
if !method.allow_offline_participation
if isnothing(model) || !has_values(model)
@ -124,7 +133,7 @@ function _aelmp_check_parameters(
)
end
end
all_units = instance.units
all_units = sc.units
# CHECK: model cannot handle non-fast-starts (MISO Phase I: can ONLY solve fast-starts)
if any(u -> u.min_uptime > 1 || u.min_downtime > 1, all_units)
error(
@ -143,19 +152,19 @@ function _aelmp_check_parameters(
end
end
function _modify_instance!(
instance::UnitCommitmentInstance,
function _modify_scenario!(
sc::UnitCommitmentScenario,
model::JuMP.Model,
method::AELMP,
)
# this function modifies the instance units (generators)
# this function modifies the sc units (generators)
if !method.allow_offline_participation
# 1. remove (if NOT allowing) the offline generators
units_to_remove = []
for unit in instance.units
for unit in sc.units
# remove based on the solved UC model result
# remove the unit if it is never on
if all(t -> value(model[:is_on][unit.name, t]) == 0, instance.time)
if all(t -> value(model[:is_on][unit.name, t]) == 0, sc.time)
# unregister from the bus
filter!(x -> x.name != unit.name, unit.bus.units)
# unregister from the reserve
@ -167,10 +176,10 @@ function _modify_instance!(
end
end
# unregister the units from the remove list
filter!(x -> !(x.name in units_to_remove), instance.units)
filter!(x -> !(x.name in units_to_remove), sc.units)
end
for unit in instance.units
for unit in sc.units
# 2. set min generation requirement to 0 by adding 0 to production curve and cost
# min_power & min_costs are vectors with dimension T
if unit.min_power[1] != 0
@ -200,5 +209,5 @@ function _modify_instance!(
unit.startup_categories =
StartupCategory[StartupCategory(0, first_startup_cost)]
end
return instance.units_by_name = Dict(g.name => g for g in instance.units)
return sc.units_by_name = Dict(g.name => g for g in sc.units)
end

@ -9,7 +9,7 @@ using JuMP
model::JuMP.Model,
method::ConventionalLMP;
optimizer,
)::OrderedDict{Tuple{String,Int},Float64}
)::OrderedDict{Tuple{String,String,Int},Float64}
Calculates conventional locational marginal prices of the given unit commitment
instance. Returns a dictionary mapping `(bus_name, time)` to the marginal price.
@ -55,15 +55,15 @@ lmp = UnitCommitment.compute_lmp(
)
# Access the LMPs
# Example: "b1" is the bus name, 1 is the first time slot
@show lmp["b1", 1]
# Example: "s1" is the scenario name, "b1" is the bus name, 1 is the first time slot
@show lmp["s1", "b1", 1]
```
"""
function compute_lmp(
model::JuMP.Model,
::ConventionalLMP;
optimizer,
)::OrderedDict{Tuple{String,Int},Float64}
)::OrderedDict{Tuple{String,String,Int},Float64}
if !has_values(model)
error("The UC model must be solved before calculating the LMPs.")
end

@ -20,7 +20,7 @@ import UnitCommitment: AELMP
# policy 1: allow offlines; consider startups
aelmp_1 =
UnitCommitment.compute_lmp(model, AELMP(), optimizer = HiGHS.Optimizer)
@test aelmp_1["B1", 1] 231.7 atol = 0.1
@test aelmp_1["s1","B1", 1] 231.7 atol = 0.1
# policy 2: do not allow offlines; but consider startups
aelmp_2 = UnitCommitment.compute_lmp(
@ -31,5 +31,5 @@ import UnitCommitment: AELMP
),
optimizer = HiGHS.Optimizer,
)
@test aelmp_2["B1", 1] 274.3 atol = 0.1
@test aelmp_2["s1","B1", 1] 274.3 atol = 0.1
end

@ -5,7 +5,7 @@
using UnitCommitment, Cbc, HiGHS, JuMP
import UnitCommitment: ConventionalLMP
function solve_lmp_testcase(path::String)
function solve_conventional_testcase(path::String)
instance = UnitCommitment.read(path)
model = UnitCommitment.build_model(
instance = instance,
@ -22,30 +22,30 @@ function solve_lmp_testcase(path::String)
return lmp
end
@testset "lmp" begin
@testset "conventional" begin
# instance 1
path = "$FIXTURES/lmp_simple_test_1.json.gz"
lmp = solve_lmp_testcase(path)
@test lmp["A", 1] == 50.0
@test lmp["B", 1] == 50.0
lmp = solve_conventional_testcase(path)
@test lmp["s1", "A", 1] == 50.0
@test lmp["s1", "B", 1] == 50.0
# instance 2
path = "$FIXTURES/lmp_simple_test_2.json.gz"
lmp = solve_lmp_testcase(path)
@test lmp["A", 1] == 50.0
@test lmp["B", 1] == 60.0
lmp = solve_conventional_testcase(path)
@test lmp["s1", "A", 1] == 50.0
@test lmp["s1", "B", 1] == 60.0
# instance 3
path = "$FIXTURES/lmp_simple_test_3.json.gz"
lmp = solve_lmp_testcase(path)
@test lmp["A", 1] == 50.0
@test lmp["B", 1] == 70.0
@test lmp["C", 1] == 100.0
lmp = solve_conventional_testcase(path)
@test lmp["s1","A", 1] == 50.0
@test lmp["s1","B", 1] == 70.0
@test lmp["s1","C", 1] == 100.0
# instance 4
path = "$FIXTURES/lmp_simple_test_4.json.gz"
lmp = solve_lmp_testcase(path)
@test lmp["A", 1] == 50.0
@test lmp["B", 1] == 70.0
@test lmp["C", 1] == 90.0
lmp = solve_conventional_testcase(path)
@test lmp["s1","A", 1] == 50.0
@test lmp["s1","B", 1] == 70.0
@test lmp["s1","C", 1] == 90.0
end

@ -40,7 +40,7 @@ FIXTURES = "$(@__DIR__)/fixtures"
include("validation/repair_test.jl")
end
@testset "lmp" begin
include("lmp/lmp_test.jl")
include("lmp/conventional_test.jl")
include("lmp/aelmp_test.jl")
end
end

Loading…
Cancel
Save