From 5f5c8b66ebf7695f62314545fa3973ad4cfc71e0 Mon Sep 17 00:00:00 2001 From: Jun He Date: Sun, 19 Mar 2023 14:28:39 -0400 Subject: [PATCH] more condition checking on AELMP --- docs/src/usage.md | 2 +- src/lmp/aelmp.jl | 62 +++++++++++++++++++++++++++++++---------------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/docs/src/usage.md b/docs/src/usage.md index d9e5f65..08fe545 100644 --- a/docs/src/usage.md +++ b/docs/src/usage.md @@ -189,7 +189,7 @@ This method has two configurable parameters: `allow_offline_participation` and ` !!! warning - 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 method does not support time-varying start-up costs. AELMPs are only calculated for the first time period if offline participation is not allowed. + 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. If offline participation is not allowed, AELMPs treats an asset to be offline if it is never on throughout all time periods. ```julia using UnitCommitment diff --git a/src/lmp/aelmp.jl b/src/lmp/aelmp.jl index f69f052..93041da 100644 --- a/src/lmp/aelmp.jl +++ b/src/lmp/aelmp.jl @@ -8,8 +8,8 @@ using JuMP function compute_lmp( model::JuMP.Model, method::AELMP; - optimizer = nothing, - ) + optimizer, + )::OrderedDict{Tuple{String,Int},Float64} Calculates the approximate extended locational marginal prices of the given unit commitment instance. @@ -25,7 +25,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. AELMPs are only calculated for the first time period if offline participation is not allowed. +3. An asset is considered offline if it is never on throughout all time periods. Arguments --------- @@ -82,7 +82,7 @@ function compute_lmp( )::OrderedDict{Tuple{String,Int},Float64} @info "Building the approximation model..." instance = deepcopy(model[:instance]) - _aelmp_check_parameters(method, model) + _aelmp_check_parameters(instance, model, method) _modify_instance!(instance, model, method) # prepare the result dictionary and solve the model @@ -111,7 +111,11 @@ function compute_lmp( return elmp end -function _aelmp_check_parameters(method::AELMP, model::JuMP.Model) +function _aelmp_check_parameters( + instance::UnitCommitmentInstance, + model::JuMP.Model, + method::AELMP, +) # CHECK: model must be solved if allow_offline_participation=false if !method.allow_offline_participation if isnothing(model) || !has_values(model) @@ -120,6 +124,29 @@ function _aelmp_check_parameters(method::AELMP, model::JuMP.Model) ) end end + all_units = instance.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( + "The minimum up/down time of all generators must be 1. AELMP only supports fast-starts.", + ) + end + if any(u -> u.initial_power > 0, all_units) + error( + "The initial power of all generators must be 0.", + ) + end + if any(u -> u.initial_status >= 0, all_units) + error( + "The initial status of all generators must be negative.", + ) + end + # CHECK: model does not support startup costs (in time series) + if any(u -> length(u.startup_categories) > 1, all_units) + error( + "The method does NOT support time-varying start-up costs.", + ) + end end function _modify_instance!( @@ -128,22 +155,25 @@ function _modify_instance!( method::AELMP, ) # this function modifies the instance units (generators) - # 1. remove (if NOT allowing) the offline generators if !method.allow_offline_participation + # 1. remove (if NOT allowing) the offline generators + units_to_remove = [] for unit in instance.units # remove based on the solved UC model result - # here, only look at the first time slot (TIME-SERIES-NOT-SUPPORTED) - if value(model[:is_on][unit.name, 1]) == 0 + # remove the unit if it is never on + if all(t -> value(model[:is_on][unit.name, t]) == 0, instance.time) # unregister from the bus filter!(x -> x.name != unit.name, unit.bus.units) # unregister from the reserve for r in unit.reserves filter!(x -> x.name != unit.name, r.units) end + # append the name to the remove list + push!(units_to_remove, unit.name) end end - # unregister the units - filter!(x -> value(model[:is_on][x.name, 1]) != 0, instance.units) + # unregister the units from the remove list + filter!(x -> !(x.name in units_to_remove), instance.units) end for unit in instance.units @@ -164,7 +194,6 @@ function _modify_instance!( end # 3. average the start-up costs (if considering) - # for now, consider first element only (TIME-SERIES-NOT-SUPPORTED) # if consider_startup_costs = false, then use the current first_startup_cost first_startup_cost = unit.startup_categories[1].cost if method.consider_startup_costs @@ -174,17 +203,8 @@ function _modify_instance!( end first_startup_cost = 0.0 # zero out the start up cost end - - # 4. other adjustments... - ### FIXME in the future - # MISO Phase I: can ONLY solve fast-starts, force all startup time to be 0 unit.startup_categories = - StartupCategory[StartupCategory(0, first_startup_cost)] - unit.initial_status = -100 - unit.initial_power = 0 - unit.min_uptime = 0 - unit.min_downtime = 0 - ### END FIXME + StartupCategory[StartupCategory(0, first_startup_cost)] end return instance.units_by_name = Dict(g.name => g for g in instance.units) end