Flatten dir structure, update docstrings

pull/27/head
Alinson S. Xavier 3 years ago
parent 34ca6952fb
commit d2e11eee42
Signed by: isoron
GPG Key ID: 0DA8E4B9E1109DCA

@ -1,4 +1,5 @@
[deps] [deps]
Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe" Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
UnitCommitment = "64606440-39ea-11e9-0f29-3303a1d3d877" UnitCommitment = "64606440-39ea-11e9-0f29-3303a1d3d877"

@ -1,4 +1,4 @@
using Documenter, UnitCommitment using Documenter, UnitCommitment, JuMP
makedocs( makedocs(
sitename="UnitCommitment.jl", sitename="UnitCommitment.jl",

@ -12,6 +12,20 @@ UnitCommitment.validate
UnitCommitment.write UnitCommitment.write
``` ```
## Locational Marginal Prices
### Conventional LMPs
```@docs
UnitCommitment.compute_lmp(::JuMP.Model,::UnitCommitment.ConventionalLMP)
```
### Approximated Extended LMPs
```@docs
UnitCommitment.AELMP
UnitCommitment.compute_lmp(::JuMP.Model,::UnitCommitment.AELMP)
```
## Modify instance ## Modify instance
```@docs ```@docs

@ -18,8 +18,6 @@ include("model/formulations/MorLatRam2013/structs.jl")
include("model/formulations/PanGua2016/structs.jl") include("model/formulations/PanGua2016/structs.jl")
include("solution/methods/XavQiuWanThi2019/structs.jl") include("solution/methods/XavQiuWanThi2019/structs.jl")
include("model/formulations/WanHob2016/structs.jl") include("model/formulations/WanHob2016/structs.jl")
include("lmp/lmp/structs.jl")
include("lmp/aelmp/structs.jl")
include("import/egret.jl") include("import/egret.jl")
include("instance/read.jl") include("instance/read.jl")
@ -59,7 +57,7 @@ include("utils/log.jl")
include("utils/benchmark.jl") include("utils/benchmark.jl")
include("validation/repair.jl") include("validation/repair.jl")
include("validation/validate.jl") include("validation/validate.jl")
include("lmp/lmp/compute.jl") include("lmp/conventional.jl")
include("lmp/aelmp/compute.jl") include("lmp/aelmp.jl")
end end

@ -3,21 +3,26 @@
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
using JuMP using JuMP
""" """
function compute_lmp( function compute_lmp(
model::JuMP.Model, model::JuMP.Model,
method::AELMP.Method; method::AELMP;
optimizer = nothing, optimizer = nothing,
) )
Calculates the approximate extended locational marginal prices of the given unit commitment instance. Calculates the approximate extended locational marginal prices of the given unit commitment instance.
The AELPM does the following three things: The AELPM does the following three things:
1. It removes the minimum generation requirement for each generator
2. It averages the start-up cost over the offer blocks for each generator
3. It relaxes all the binary constraints and integrality
Returns a dictionary of AELMPs. Each key is usually a tuple of "Bus name" and time index.
NOTE: this approximation method is not fully developed. The implementation is based on MISO Phase I only. 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
3. It relaxes all integrality constraints
Returns a dictionary mapping `(bus_name, time)` to the marginal price.
WARNING: This approximation method is not fully developed. The implementation is based on MISO Phase I only.
1. It only supports Fast Start resources. More specifically, the minimum up/down time has to be zero. 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 series of start-up costs. 2. The method does NOT support time series of start-up costs.
3. The method can only calculate for the first time slot if allow_offline_participation=false. 3. The method can only calculate for the first time slot if allow_offline_participation=false.
@ -29,7 +34,7 @@ Arguments
the UnitCommitment model, must be solved before calling this function if offline participation is not allowed. the UnitCommitment model, must be solved before calling this function if offline participation is not allowed.
- `method`: - `method`:
the AELMP method, must be specified. the AELMP method.
- `optimizer`: - `optimizer`:
the optimizer for solving the LP problem. the optimizer for solving the LP problem.
@ -38,60 +43,77 @@ Examples
-------- --------
```julia ```julia
using UnitCommitment using UnitCommitment
using Cbc
using HiGHS using HiGHS
import UnitCommitment: import UnitCommitment: AELMP
AELMP
# Read benchmark instance # Read benchmark instance
instance = UnitCommitment.read("instance.json") instance = UnitCommitment.read_benchmark("matpower/case118/2017-02-01")
# Construct model (using state-of-the-art defaults) # Build the model
model = UnitCommitment.build_model( model = UnitCommitment.build_model(
instance = instance, instance = instance,
optimizer = Cbc.Optimizer, optimizer = HiGHS.Optimizer,
variable_names = true,
) )
# Get the AELMP with the default policy: # Optimize the model
# 1. Offline generators are allowed to participate in pricing
# 2. Start-up costs are considered.
# DO NOT use Cbc as the optimizer here. Cbc does not support dual values.
my_aelmp_default = UnitCommitment.compute_lmp(
model, # pre-solving is optional if allowing offline participation
AELMP.Method(),
optimizer = HiGHS.Optimizer
)
# Get the AELMPs with an alternative policy
# 1. Offline generators are NOT allowed to participate in pricing
# 2. Start-up costs are considered.
# UC model must be solved first if offline generators are NOT allowed
UnitCommitment.optimize!(model) UnitCommitment.optimize!(model)
# then call the AELMP method # Compute the AELMPs
my_aelmp_alt = UnitCommitment.compute_lmp( aelmp = UnitCommitment.compute_lmp(
model, # pre-solving is required here model,
AELMP.Method( AELMP(
allow_offline_participation=false, allow_offline_participation = false,
consider_startup_costs=true consider_startup_costs = true
), ),
optimizer = HiGHS.Optimizer optimizer = HiGHS.Optimizer
) )
# Accessing the 'my_aelmp_alt' dictionary # Access the AELMPs
# Example: "b1" is the bus name, 1 is the first time slot # Example: "b1" is the bus name, 1 is the first time slot
@show my_aelmp_alt["b1", 1] @show aelmp["b1", 1]
``` ```
""" """
function compute_lmp(
model::JuMP.Model,
method::AELMP;
optimizer,
)::OrderedDict{Tuple{String,Int},Float64}
@info "Calculating the AELMP..."
@info "Building the approximation model..."
instance = deepcopy(model[:instance])
_preset_aelmp_parameters!(method, model)
_modify_instance!(instance, model, method)
# prepare the result dictionary and solve the model
elmp = OrderedDict()
@info "Solving the approximation model."
approx_model = build_model(instance=instance, variable_names=true)
# relax the binary constraint, and relax integrality
for v in all_variables(approx_model)
if is_binary(v)
unset_binary(v)
end
end
relax_integrality(approx_model)
set_optimizer(approx_model, optimizer)
# solve the model
set_silent(approx_model)
optimize!(approx_model)
# access the dual values
@info "Getting dual values (AELMPs)."
for (key, val) in approx_model[:eq_net_injection]
elmp[key] = dual(val)
end
return elmp
end
function _preset_aelmp_parameters!( function _preset_aelmp_parameters!(
method::AELMP.Method, method::AELMP,
model::JuMP.Model model::JuMP.Model
) )
# this function corrects the allow_offline_participation parameter to match the model status # this function corrects the allow_offline_participation parameter to match the model status
@ -125,7 +147,7 @@ end
function _modify_instance!( function _modify_instance!(
instance::UnitCommitmentInstance, instance::UnitCommitmentInstance,
model::JuMP.Model, model::JuMP.Model,
method::AELMP.Method method::AELMP
) )
# this function modifies the instance units (generators) # this function modifies the instance units (generators)
# 1. remove (if NOT allowing) the offline generators # 1. remove (if NOT allowing) the offline generators
@ -182,50 +204,4 @@ function _modify_instance!(
### END FIXME ### END FIXME
end end
instance.units_by_name = Dict(g.name => g for g in instance.units) instance.units_by_name = Dict(g.name => g for g in instance.units)
end
function compute_lmp(
model::JuMP.Model,
method::AELMP.Method;
optimizer = nothing
)
# Error if a linear optimizer is not specified
if isnothing(optimizer)
@error "Please supply a linear optimizer."
return nothing
end
@info "Calculating the AELMP..."
@info "Building the approximation model..."
# get the instance and make a deep copy
instance = deepcopy(model[:instance])
# preset the method to match the model status (solved, unsolved, not supplied)
_preset_aelmp_parameters!(method, model)
# modify the instance (generator)
_modify_instance!(instance, model, method)
# prepare the result dictionary and solve the model
elmp = OrderedDict()
@info "Solving the approximation model."
approx_model = build_model(instance=instance, variable_names=true)
# relax the binary constraint, and relax integrality
for v in all_variables(approx_model)
if is_binary(v)
unset_binary(v)
end
end
relax_integrality(approx_model)
set_optimizer(approx_model, optimizer)
# solve the model
# set_silent(approx_model)
optimize!(approx_model)
# access the dual values
@info "Getting dual values (AELMPs)."
for (key, val) in approx_model[:eq_net_injection]
elmp[key] = dual(val)
end
return elmp
end end

@ -1,41 +0,0 @@
# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
module AELMP
import ..PricingMethod
"""
mutable struct Method
allow_offline_participation::Bool,
consider_startup_costs::Bool
end
------
- `allow_offline_participation`:
defaults to true.
If true, offline assets are allowed to participate in pricing.
- `consider_startup_costs`:
defaults to true.
If true, the start-up costs are averaged over each unit production; otherwise the production costs stay the same.
"""
mutable struct Method <: PricingMethod
allow_offline_participation::Bool
consider_startup_costs::Bool
function Method(;
allow_offline_participation::Bool = true,
consider_startup_costs::Bool = true
)
return new(
allow_offline_participation,
consider_startup_costs
)
end
end
end

@ -7,13 +7,12 @@ using JuMP
""" """
function compute_lmp( function compute_lmp(
model::JuMP.Model, model::JuMP.Model,
method::LMP.Method; method::ConventionalLMP;
optimizer = nothing optimizer,
) )::OrderedDict{Tuple{String,Int},Float64}
Calculates the locational marginal prices of the given unit commitment instance. Calculates conventional locational marginal prices of the given unit commitment
Returns a dictionary of LMPs. Each key is usually a tuple of "Bus name" and time index. instance. Returns a dictionary mapping `(bus_name, time)` to the marginal price.
Returns nothing if there is an error in solving the LMPs.
Arguments Arguments
--------- ---------
@ -22,7 +21,7 @@ Arguments
the UnitCommitment model, must be solved before calling this function. the UnitCommitment model, must be solved before calling this function.
- `method`: - `method`:
the LMP method, must be specified. the LMP method.
- `optimizer`: - `optimizer`:
the optimizer for solving the LP problem. the optimizer for solving the LP problem.
@ -31,57 +30,40 @@ Examples
-------- --------
```julia ```julia
using UnitCommitment using UnitCommitment
using Cbc
using HiGHS using HiGHS
import UnitCommitment: import UnitCommitment: ConventionalLMP
LMP
# Read benchmark instance # Read benchmark instance
instance = UnitCommitment.read("instance.json") instance = UnitCommitment.read_benchmark("matpower/case118/2018-01-01")
# Construct model (using state-of-the-art defaults) # Build the model
model = UnitCommitment.build_model( model = UnitCommitment.build_model(
instance = instance, instance = instance,
optimizer = Cbc.Optimizer, optimizer = HiGHS.Optimizer,
) )
# Get the LMPs before solving the UC model # Optimize the model
# Error messages will be displayed and the returned value is nothing.
# lmp = UnitCommitment.compute_lmp(model, LMP.Method(), optimizer = HiGHS.Optimizer) # DO NOT RUN
UnitCommitment.optimize!(model) UnitCommitment.optimize!(model)
# Get the LMPs after solving the UC model (the correct way) # Compute the LMPs using the conventional method
# DO NOT use Cbc as the optimizer here. Cbc does not support dual values. lmp = UnitCommitment.compute_lmp(
# Compute regular LMP
my_lmp = UnitCommitment.compute_lmp(
model, model,
LMP.Method(), ConventionalLMP(),
optimizer = HiGHS.Optimizer, optimizer = HiGHS.Optimizer,
) )
# Accessing the 'my_lmp' dictionary # Access the LMPs
# Example: "b1" is the bus name, 1 is the first time slot # Example: "b1" is the bus name, 1 is the first time slot
@show my_lmp["b1", 1] @show lmp["b1", 1]
``` ```
""" """
function compute_lmp( function compute_lmp(
model::JuMP.Model, model::JuMP.Model,
method::LMP.Method; ::ConventionalLMP;
optimizer = nothing optimizer,
) )::OrderedDict{Tuple{String,Int},Float64}
# Error if a linear optimizer is not specified
if isnothing(optimizer)
@error "Please supply a linear optimizer."
return nothing
end
# Validate model, the UC model must be solved beforehand # Validate model, the UC model must be solved beforehand
if !has_values(model) if !has_values(model)
@error "The UC model must be solved before calculating the LMPs." @error "The UC model must be solved before calculating the LMPs."

@ -1,18 +0,0 @@
# UnitCommitment.jl: Optimization Package for Security-Constrained Unit Commitment
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
"""
Formulation described in:
Arroyo, J. M., & Conejo, A. J. (2000). Optimal response of a thermal unit
to an electricity spot market. IEEE Transactions on power systems, 15(3),
1098-1104. DOI: https://doi.org/10.1109/59.871739
"""
module LMP
import ..PricingMethod
struct Method <: PricingMethod end
end

@ -3,3 +3,26 @@
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
abstract type PricingMethod end abstract type PricingMethod end
struct ConventionalLMP <: PricingMethod end
"""
struct AELMP <: PricingMethod
allow_offline_participation::Bool = true
consider_startup_costs::Bool = true
end
Approximate Extended LMPs.
Arguments
---------
- `allow_offline_participation`:
If true, offline assets are allowed to participate in pricing.
- `consider_startup_costs`:
If true, the start-up costs are averaged over each unit production; otherwise the production costs stay the same.
"""
Base.@kwdef struct AELMP <: PricingMethod
allow_offline_participation::Bool = true
consider_startup_costs::Bool = true
end

@ -21,7 +21,7 @@ import UnitCommitment:
# policy 1: allow offlines; consider startups # policy 1: allow offlines; consider startups
aelmp_1 = UnitCommitment.compute_lmp( aelmp_1 = UnitCommitment.compute_lmp(
model, model,
AELMP.Method(), AELMP(),
optimizer=HiGHS.Optimizer optimizer=HiGHS.Optimizer
) )
@test aelmp_1["B1", 1] 231.7 atol = 0.1 @test aelmp_1["B1", 1] 231.7 atol = 0.1
@ -29,7 +29,7 @@ import UnitCommitment:
# policy 2: do not allow offlines; but consider startups # policy 2: do not allow offlines; but consider startups
aelmp_2 = UnitCommitment.compute_lmp( aelmp_2 = UnitCommitment.compute_lmp(
model, model,
AELMP.Method( AELMP(
allow_offline_participation=false, allow_offline_participation=false,
consider_startup_costs=true consider_startup_costs=true
), ),

@ -3,8 +3,7 @@
# Released under the modified BSD license. See COPYING.md for more details. # Released under the modified BSD license. See COPYING.md for more details.
using UnitCommitment, Cbc, HiGHS, JuMP using UnitCommitment, Cbc, HiGHS, JuMP
import UnitCommitment: import UnitCommitment: ConventionalLMP
LMP
function solve_lmp_testcase(path::String) function solve_lmp_testcase(path::String)
instance = UnitCommitment.read(path) instance = UnitCommitment.read(path)
@ -13,13 +12,11 @@ function solve_lmp_testcase(path::String)
optimizer = Cbc.Optimizer, optimizer = Cbc.Optimizer,
variable_names = true, variable_names = true,
) )
# set silent, solve the UC
JuMP.set_silent(model) JuMP.set_silent(model)
UnitCommitment.optimize!(model) UnitCommitment.optimize!(model)
# get the lmp
lmp = UnitCommitment.compute_lmp( lmp = UnitCommitment.compute_lmp(
model, model,
LMP.Method(), ConventionalLMP(),
optimizer=HiGHS.Optimizer, optimizer=HiGHS.Optimizer,
) )
return lmp return lmp

Loading…
Cancel
Save