pull/55/merge
type-null 2 weeks ago committed by GitHub
commit a430b07b62
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -24,7 +24,8 @@ This section describes system-wide parameters, such as power balance penalty, an
| `Time step (min)` | Length of each time step (in minutes). Must be a divisor of 60 (e.g. 60, 30, 20, 15, etc). | `60` | No | No | | `Time step (min)` | Length of each time step (in minutes). Must be a divisor of 60 (e.g. 60, 30, 20, 15, etc). | `60` | No | No |
| `Power balance penalty ($/MW)` | Penalty for system-wide shortage or surplus in production (in $/MW). This is charged per time step. For example, if there is a shortage of 1 MW for three time steps, three times this amount will be charged. | `1000.0` | No | Yes | | `Power balance penalty ($/MW)` | Penalty for system-wide shortage or surplus in production (in $/MW). This is charged per time step. For example, if there is a shortage of 1 MW for three time steps, three times this amount will be charged. | `1000.0` | No | Yes |
| `Scenario name` | Name of the scenario. | `"s1"` | No | --- | | `Scenario name` | Name of the scenario. | `"s1"` | No | --- |
| `Scenario weight` | Weight of the scenario. The scenario weight can be any positive real number, that is, it does not have to be between zero and one. The package normalizes the weights to ensure that the probability of all scenarios sum up to one. | 1.0 | No | --- | | `Scenario weight` | Weight of the scenario. The scenario weight can be any positive real number, that is, it does not have to be between zero and one. The package normalizes the weights to ensure that the probability of all scenarios sum up to one. | `1.0` | No | --- |
| `Operation cost weight` | Weighting factor to make operation costs comparable to investment costs. Required for transmission expansion planning problem. | `1.0` | No | --- |
#### Example #### Example
@ -35,7 +36,8 @@ This section describes system-wide parameters, such as power balance penalty, an
"Time horizon (h)": 4, "Time horizon (h)": 4,
"Power balance penalty ($/MW)": 1000.0, "Power balance penalty ($/MW)": 1000.0,
"Scenario name": "s1", "Scenario name": "s1",
"Scenario weight": 0.5 "Scenario weight": 0.5,
"Operation cost weight": 1.0
} }
} }
``` ```
@ -89,6 +91,7 @@ This section describes all generators in the system. Two types of units can be s
| `Must run?` | If `true`, the generator should be committed, even if that is not economical (Boolean). | `false` | Yes | Yes | | `Must run?` | If `true`, the generator should be committed, even if that is not economical (Boolean). | `false` | Yes | Yes |
| `Reserve eligibility` | List of reserve products this generator is eligibe to provide. By default, the generator is not eligible to provide any reserves. | `[]` | No | Yes | | `Reserve eligibility` | List of reserve products this generator is eligibe to provide. By default, the generator is not eligible to provide any reserves. | `[]` | No | Yes |
| `Commitment status` | List of commitment status over the time horizon. At time `t`, if `true`, the generator must be commited at that time period; if `false`, the generator must not be commited at that time period. If `null` at time `t`, the generator's commitment status is then decided by the model. By default, the status is a list of `null` values. | `null` | Yes | Yes | | `Commitment status` | List of commitment status over the time horizon. At time `t`, if `true`, the generator must be commited at that time period; if `false`, the generator must not be commited at that time period. If `null` at time `t`, the generator's commitment status is then decided by the model. By default, the status is a list of `null` values. | `null` | Yes | Yes |
| `Investment cost ($)` | Cost to build a candidate generation unit. $0.0 for existing units. | `0.0` | No | No |
#### Profiled Units #### Profiled Units
@ -99,7 +102,7 @@ This section describes all generators in the system. Two types of units can be s
| `Cost ($/MW)` | Cost incurred for serving each MW of power by this generator. | Required | Yes | Yes | | `Cost ($/MW)` | Cost incurred for serving each MW of power by this generator. | Required | Yes | Yes |
| `Minimum power (MW)` | Minimum amount of power this generator may supply. | `0.0` | Yes | Yes | | `Minimum power (MW)` | Minimum amount of power this generator may supply. | `0.0` | Yes | Yes |
| `Maximum power (MW)` | Maximum amount of power this generator may supply. | Required | Yes | Yes | | `Maximum power (MW)` | Maximum amount of power this generator may supply. | Required | Yes | Yes |
| `Investment cost ($)`| Cost to build a candidate generation unit. $0.0 for existing units. | `0.0`. | No | No |
#### Production costs and limits #### Production costs and limits
Production costs are represented as piecewise-linear curves. Figure 1 shows an example cost curve with three segments, where it costs \$1400, \$1600, \$2200 and \$2400 to generate, respectively, 100, 110, 130 and 135 MW of power. To model this generator, `Production cost curve (MW)` should be set to `[100, 110, 130, 135]`, and `Production cost curve ($)` should be set to `[1400, 1600, 2200, 2400]`. Production costs are represented as piecewise-linear curves. Figure 1 shows an example cost curve with three segments, where it costs \$1400, \$1600, \$2200 and \$2400 to generate, respectively, 100, 110, 130 and 135 MW of power. To model this generator, `Production cost curve (MW)` should be set to `[100, 110, 130, 135]`, and `Production cost curve ($)` should be set to `[1400, 1600, 2200, 2400]`.
@ -158,7 +161,8 @@ Note that this curve also specifies the production limits. Specifically, the fir
"Type": "Profiled", "Type": "Profiled",
"Minimum power (MW)": 10.0, "Minimum power (MW)": 10.0,
"Maximum power (MW)": 120.0, "Maximum power (MW)": 120.0,
"Cost ($/MW)": 100.0 "Cost ($/MW)": 100.0,
"Investment cost ($)": 3000000.0
} }
} }
} }
@ -276,6 +280,8 @@ This section describes the characteristics of transmission system, such as its t
| `Normal flow limit (MW)` | Maximum amount of power (in MW) allowed to flow through the line when the system is in its regular, fully-operational state. | `+inf` | Yes | Yes | | `Normal flow limit (MW)` | Maximum amount of power (in MW) allowed to flow through the line when the system is in its regular, fully-operational state. | `+inf` | Yes | Yes |
| `Emergency flow limit (MW)` | Maximum amount of power (in MW) allowed to flow through the line when the system is in degraded state (for example, after the failure of another transmission line). | `+inf` | Y | Yes | | `Emergency flow limit (MW)` | Maximum amount of power (in MW) allowed to flow through the line when the system is in degraded state (for example, after the failure of another transmission line). | `+inf` | Y | Yes |
| `Flow limit penalty ($/MW)` | Penalty for violating the flow limits of the transmission line (in $/MW). This is charged per time step. For example, if there is a thermal violation of 1 MW for three time steps, then three times this amount will be charged. | `5000.0` | Yes | Yes | | `Flow limit penalty ($/MW)` | Penalty for violating the flow limits of the transmission line (in $/MW). This is charged per time step. For example, if there is a thermal violation of 1 MW for three time steps, then three times this amount will be charged. | `5000.0` | Yes | Yes |
| `Investment cost ($)` | Cost to build a candidate transmission line. $0 for existing lines. | `0.0` | No | No |
| `Max number of parallel circuits`| Maximum number of lines can be built in this corridor. | `1` | No | No |
#### Example #### Example
@ -288,7 +294,9 @@ This section describes the characteristics of transmission system, such as its t
"Susceptance (S)": 29.49686, "Susceptance (S)": 29.49686,
"Normal flow limit (MW)": 15000.0, "Normal flow limit (MW)": 15000.0,
"Emergency flow limit (MW)": 20000.0, "Emergency flow limit (MW)": 20000.0,
"Flow limit penalty ($/MW)": 5000.0 "Flow limit penalty ($/MW)": 5000.0,
"Investment cost ($)": 3000000.0,
"Max number of parallel circuits": 2
} }
} }
} }

@ -264,6 +264,23 @@ Test cases used in [TeLuSa19]. These instances are similar to OR-LIB/UC, in the
| `tejada19/UC_168h_192g` | 168 | 1 | 192 | 0 | 0 | [TeLuSa19] | | `tejada19/UC_168h_192g` | 168 | 1 | 192 | 0 | 0 | [TeLuSa19] |
| `tejada19/UC_168h_199g` | 168 | 1 | 199 | 0 | 0 | [TeLuSa19] | | `tejada19/UC_168h_199g` | 168 | 1 | 199 | 0 | 0 | [TeLuSa19] |
### Transmission and Expansion Planning Instances
We curate a list of popular [MuSe21] transmission and expansion planning (TEP) instances used in academia paper. These TEP instances follow [UCJL format](format.md), with information on candidate transmission lines and investment costs.
| Name | Buses | Generators | Lines | Generator Expension? | Transmission Expension? | Power Balance Penalty | References |
| ---------------------- | ----- | ---------- | ----- | -------------------- | ----------------------- | --------------------- | ---------- |
| `tep/garver6` | 6 | 3 | 6 | No | Yes | No | [Ga70] |
| `tep/ieee14` | 14 | 2 | 20 | No | Yes | Yes | [ZhDoLi09] |
| `tep/ieee24` | 24 | 32 | 35 | No | Yes | Yes | [FaHi03] |
| `tep/ieee30` | 30 | 6 | 41 | No | Yes | No | [AlDeAm13] |
| `tep/ieee118` | 118 | 70 | 186 | Yes | Yes | 1 | [KhShKa10] |
| `tep/ieee300` | 300 | 60 | 411 | No | Yes | Yes | [ZhChZa17] |
| `tep/south_brazilian` | 242 | 53 | 467 | No | Yes | 1000 | [HaMoGa00] |
| `tep/north_brazilian` | 87 | 14 | 299 | No | Yes | 1000 | [RoMoGa02] |
| `tep/polish2383` | 2383 | 326 | 2896 | No | Yes | Yes | [ZhChZa17] |
## References ## References
- [UCJL] **Alinson S. Xavier, Aleksandr M. Kazachkov, Ogün Yurdakul, Feng Qiu.** "UnitCommitment.jl: A Julia/JuMP Optimization Package for Security-Constrained Unit Commitment (Version 0.3)". Zenodo (2022). [DOI: 10.5281/zenodo.4269874](https://doi.org/10.5281/zenodo.4269874) - [UCJL] **Alinson S. Xavier, Aleksandr M. Kazachkov, Ogün Yurdakul, Feng Qiu.** "UnitCommitment.jl: A Julia/JuMP Optimization Package for Security-Constrained Unit Commitment (Version 0.3)". Zenodo (2022). [DOI: 10.5281/zenodo.4269874](https://doi.org/10.5281/zenodo.4269874)
@ -286,4 +303,22 @@ Test cases used in [TeLuSa19]. These instances are similar to OR-LIB/UC, in the
- [FrGe06] **A. Frangioni, C. Gentile.** "Solving nonlinear single-unit commitment problems with ramping constraints" Operations Research 54(4), p. 767 - 775, 2006. [DOI: 10.1287/opre.1060.0309](https://doi.org/10.1287/opre.1060.0309) - [FrGe06] **A. Frangioni, C. Gentile.** "Solving nonlinear single-unit commitment problems with ramping constraints" Operations Research 54(4), p. 767 - 775, 2006. [DOI: 10.1287/opre.1060.0309](https://doi.org/10.1287/opre.1060.0309)
- [TeLuSa19] **D. A. Tejada-Arango, S. Lumbreras, P. Sanchez-Martin and A. Ramos.** "Which Unit-Commitment Formulation is Best? A Systematic Comparison," in IEEE Transactions on Power Systems. [DOI: 10.1109/TPWRS.2019.2962024](https://ieeexplore.ieee.org/document/8941313/). - [TeLuSa19] **D. A. Tejada-Arango, S. Lumbreras, P. Sanchez-Martin and A. Ramos.** "Which unit-commitment formulation is best? A systematic comparison," in IEEE Transactions on Power Systems. [DOI: 10.1109/TPWRS.2019.2962024](https://ieeexplore.ieee.org/document/8941313/).
- [MuSe21] **Selçuk Mutlu and Ercan Şenyiğit.** "Literature review of transmission expansion planning problem test systems: detailed analysis of IEEE-24," in Electric Power Systems Research 201 (2021). [DOI: 10.1016/j.epsr.2021.107543](https://doi.org/10.1016/j.epsr.2021.107543)
- [Ga70] **Len L. Garver.** "Transmission network estimation using linear programming," in IEEE Transactions on Power Apparatus and Systems, vol. PAS-89, no. 7, pp. 1688-1697, Sept. 1970. [DOI: 10.1109/TPAS.1970.292825](https://ieeexplore.ieee.org/abstract/document/4074249)
- [ZhDoLi09] **J. Zhao, Z. Dong, P. Lindsay and K. Wong.** "Flexible transmission expansion planning with uncertainties in an electricity market," IEEE Transactions on Power Systems, Vol. 24, No. 1, February 2009. [DOI: 10.1109/TPWRS.2008.2008681](https://doi.org/10.1109/TPWRS.2008.2008681)
- [FaHi03] **Risheng Fang, David J. Hill.** "A new strategy for transmission expansion in competitive electricity markets." IEEE Transactions on power systems 18.1 (2003): 374-380. [DOI: 10.1109/TPWRS.2002.807083](https://ieeexplore.ieee.org/document/1178822)
- [AlDeAm13] **Behnam Alizadeh, Shahab Dehghan, Nima Amjady, Shahram Jadid and Ahad Kazemi.** "Robust transmission system expansion considering planning uncertainties," IET Generation, Transmission & Distribution (2013). [DOI: 10.1049/iet-gtd.2012.0137](https://doi.org/10.1049/iet-gtd.2012.0137)
- [KhShKa10] **Amin Khodaei, Mohammad Shahidehpour and Saeed Kamalinia.** "Transmission switching in expansion planning," IEEE Transactions on Power Systems, VOL. 25, NO. 3, AUGUST 2010. [DOI: 10.1109/TPWRS.2009.2039946](https://doi.org/10.1109/TPWRS.2009.2039946)
- [ZhChZa17] **J. Zhan, C. Chung and A. Zare.** "A fast solution method for stochastic transmission expansion planning," IEEE Transactions on Power Systems, Vol. 32, No. 6, Novermber 2017. [DOI: 10.1109/TPWRS.2017.2665695](https://doi.org/10.1109/TPWRS.2017.2665695)
- [HaMoGa00] **S. Haffner, A. Monticelli, A. Garcia, J. Mantovani and R. Romero.** "Branch and bound algorithm for transmission system expansion planning using a transportation model," IEE Proceedings - Generation Transmission and Distribution, Vol. 147, No. 3, May 2000. [DOI: 10.1049/ip-gtd:20000337](https://doi.org/10.1049/ip-gtd:20000337)
- [RoMoGa02] **R. Romero, A. Monticelli, A. Garcia and S. Haffner.** "Test systems and mathematical models for transmission network expansion planning," IEEE Proceeding - Generation Transmission and Distribution, Vol. 149, No. 1, January 2002. [DOI: 10.1049/ip-gtd:20020026](https://doi.org/10.1049/ip-gtd:20020026)

@ -338,6 +338,7 @@ function _from_json(json; repair = true)::UnitCommitmentScenario
timeseries(scalar(dict["Minimum power (MW)"], default = 0.0)), timeseries(scalar(dict["Minimum power (MW)"], default = 0.0)),
timeseries(dict["Maximum power (MW)"]), timeseries(dict["Maximum power (MW)"]),
timeseries(dict["Cost (\$/MW)"]), timeseries(dict["Cost (\$/MW)"]),
scalar(dict["Investment cost (\$)"], default = 0.0),
) )
push!(bus.profiled_units, pu) push!(bus.profiled_units, pu)
push!(profiled_units, pu) push!(profiled_units, pu)
@ -367,6 +368,8 @@ function _from_json(json; repair = true)::UnitCommitmentScenario
dict["Flow limit penalty (\$/MW)"], dict["Flow limit penalty (\$/MW)"],
default = [5000.0 for t in 1:T], default = [5000.0 for t in 1:T],
), ),
scalar(dict["Investment cost (\$)"], default = 0.0),
scalar(dict["Maximum number of copies"], default = 1),
) )
name_to_line[line_name] = line name_to_line[line_name] = line
push!(lines, line) push!(lines, line)

@ -60,6 +60,8 @@ mutable struct TransmissionLine
normal_flow_limit::Vector{Float64} normal_flow_limit::Vector{Float64}
emergency_flow_limit::Vector{Float64} emergency_flow_limit::Vector{Float64}
flow_limit_penalty::Vector{Float64} flow_limit_penalty::Vector{Float64}
invest::Float64 #TODO: make this a vector
max_copy::Int
end end
mutable struct Contingency mutable struct Contingency
@ -81,6 +83,7 @@ mutable struct ProfiledUnit
min_power::Vector{Float64} min_power::Vector{Float64}
max_power::Vector{Float64} max_power::Vector{Float64}
cost::Vector{Float64} cost::Vector{Float64}
invest::Float64
end end
mutable struct StorageUnit mutable struct StorageUnit

@ -59,3 +59,9 @@ function _setup_transmission(
sc.lodf = lodf sc.lodf = lodf
return return
end end
_setup_transmission(
::PhaseAngleFormulation,
::UnitCommitmentScenario,
) = nothing

@ -0,0 +1,110 @@
# 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.
function _add_planning_unified!(
model::JuMP.Model,
pu::ProfiledUnit,
lm::TransmissionLine,
sc::UnitCommitmentScenario,
)::Nothing
invest_unit = _init(model, :invest_unit)
invest_line = _init(model, :invest_line)
eq_invest_unit_history = _init(model, :eq_invest_unit_history)
eq_invest_line_history = _init(model, :eq_invest_line_history)
eq_invest_unit_capacity = _init(model, :eq_invest_unit_capacity)
# t == 0
# TODO: If investment cost is zero, skip creating these investment variables
invest_unit[sc.name, pu.name, 0] = @variable(
model,
binary = true,
fixed = pu.invest == 0.0
)
invest_line[sc.name, lm.name, 0] = Int(lm.invest == 0.0)
for t in 1:model[:instance].time
# Decision variable
invest_unit[sc.name, pu.name, t] = @variable(model, binary = true)
invest_line[sc.name, lm.name, t] = @variable(model, binary = true)
# Objective function terms
add_to_expression!(
model[:obj],
invest_unit[sc.name, pu.name, t] - invest_unit[sc.name, pu.name, t-1],
pu.invest[t] * sc.probability,
)
add_to_expression!(
model[:obj],
invest_line[sc.name, lm.name, t] - invest_line[sc.name, lm.name, t-1],
lm.invest[t] * sc.probability,
)
# Investment constraints
# (1c) in the paper
eq_invest_unit_history[sc.name, pu.name, t] = @constraint(
model,
invest_unit[sc.name, pu.name, t-1] <= invest_unit[sc.name, pu.name, t]
)
# (1d) in the paper
eq_invest_line_history[sc.name, lm.name, t] = @constraint(
model,
invest_line[sc.name, lm.name, t-1] <= invest_line[sc.name, lm.name, t]
)
# (1h) in the paper TODO: separate this to two halves
eq_invest_unit_capacity[sc.name, pu.name, t] = @constraint(
model,
pu.min_power[t] * invest_unit[sc.name, pu.name, t] <=
model[:prod_above][sc.name, pu.name, t] <=
pu.max_power[t] * invest_unit[sc.name, pu.name, t]
)
end
return
end
# TODO: add max_copy constraint
function _add_planning_new_lines!(
model::JuMP.Model,
lm::TransmissionLine,
bs::Bus,
f::PhaseAngleFormulation,
sc::UnitCommitmentScenario,
)::Nothing
θ = _init(model, :theta)
flow = _init(model, :flow)
invest_line = model[:invest_line]
eq_invest_line_flow_a = _init(model, :eq_invest_line_flow_a)
eq_invest_line_flow_b = _init(model, :eq_invest_line_flow_b)
eq_invest_line_flow_limit = _init(model, :eq_invest_line_flow_limit)
bigM = 1e6 # TODO: make this a parameter
for t in 1:model[:instance].time
# TODO: add bus field: phase_angle_limit? give default
θ[sc.name, bs.name, t] = @variable(model, lower_bound = -pi, upper_bound = pi)
flow[sc.name, lm.name, t] = @variable(model)
# (2b)
eq_invest_line_flow_a[sc.name, lm.name, t] = @constraint(
model,
model[:flow][sc.name, lm.name, t] <=
lm.susceptance * (θ[sc.name, lm.source.name, t] - θ[sc.name, lm.target.name, t])
+ bigM * (1 - invest_line[sc.name, lm.name, t])
)
# (2c)
eq_invest_line_flow_b[sc.name, lm.name, t] = @constraint(
model,
model[:flow][sc.name, lm.name, t] >=
lm.susceptance * (θ[sc.name, lm.source.name, t] - θ[sc.name, lm.target.name, t])
- bigM * (1 - invest_line[sc.name, lm.name, t])
)
# (2d)
eq_invest_line_flow_limit[sc.name, lm.name, t] = @constraint(
model,
-lm.normal_flow_limit[t] * invest_line[sc.name, lm.name, t] <=
model[:flow][sc.name, lm.name, t] <=
lm.normal_flow_limit[t] * invest_line[sc.name, lm.name, t]
)
end
return
end

@ -97,3 +97,14 @@ struct ShiftFactorsFormulation <: TransmissionFormulation
return new(isf_cutoff, lodf_cutoff, precomputed_isf, precomputed_lodf) return new(isf_cutoff, lodf_cutoff, precomputed_isf, precomputed_lodf)
end end
end end
"""
struct PhaseAngleFormulation <: TransmissionFormulation
end
Transmission formulation based on susceptance (b).
Constraints are enforced in a lazy way.
"""
struct PhaseAngleFormulation <: TransmissionFormulation
end

@ -0,0 +1,298 @@
{
"Parameters": {
"Version": "0.4",
"Time horizon (h)": 1,
"Power balance penalty ($/MW)": 1000.0,
"Scenario name": "s1",
"Scenario weight": 1.0,
"Operation cost weight": 1.0
},
"Buses": {
"b1": {
"Load (MW)": 80.0
},
"b2": {
"Load (MW)": 240.0
},
"b3": {
"Load (MW)": 40.0
},
"b4": {
"Load (MW)": 160.0
},
"b5": {
"Load (MW)": 240.0
},
"b6": {
"Load (MW)": 0.0
}
},
"Generators": {
"gen1": {
"Bus": "b1",
"Type": "Profiled",
"Maximum power (MW)": 150.0,
"Cost ($/MW)": 0.0
},
"gen2": {
"Bus": "b3",
"Type": "Profiled",
"Maximum power (MW)": 360.0,
"Cost ($/MW)": 0.0
},
"gen3": {
"Bus": "b6",
"Type": "Profiled",
"Maximum power (MW)": 600.0,
"Cost ($/MW)": 0.0
}
},
"Transmission lines": {
"l1": {
"Source bus": "b1",
"Target bus": "b2",
"Susceptance (S)": 2.5,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 0.0,
"Guide Number": 40.0,
"Reactance (p.u.)": 0.4,
"Resistance (p.u.)": 0.1,
"Length (miles)": 40.0
},
"l2": {
"Source bus": "b1",
"Target bus": "b4",
"Susceptance (S)": 1.6666666666666667,
"Normal flow limit (MW)": 80.0,
"Investment cost ($)": 0.0,
"Guide Number": 60.0,
"Reactance (p.u.)": 0.6,
"Resistance (p.u.)": 0.15,
"Length (miles)": 60.0
},
"l3": {
"Source bus": "b1",
"Target bus": "b5",
"Susceptance (S)": 5.0,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 0.0,
"Guide Number": 20.0,
"Reactance (p.u.)": 0.2,
"Resistance (p.u.)": 0.05,
"Length (miles)": 20.0
},
"l4": {
"Source bus": "b2",
"Target bus": "b3",
"Susceptance (S)": 5.0,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 0.0,
"Guide Number": 20.0,
"Reactance (p.u.)": 0.2,
"Resistance (p.u.)": 0.05,
"Length (miles)": 20.0
},
"l5": {
"Source bus": "b2",
"Target bus": "b4",
"Susceptance (S)": 2.5,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 0.0,
"Guide Number": 40.0,
"Reactance (p.u.)": 0.4,
"Resistance (p.u.)": 0.1,
"Length (miles)": 40.0
},
"l6": {
"Source bus": "b3",
"Target bus": "b5",
"Susceptance (S)": 5.0,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 0.0,
"Guide Number": 20.0,
"Reactance (p.u.)": 0.2,
"Resistance (p.u.)": 0.05,
"Length (miles)": 20.0
},
"new_l7": {
"Source bus": "b1",
"Target bus": "b2",
"Susceptance (S)": 2.5,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 40000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 40.0,
"Reactance (p.u.)": 0.4,
"Resistance (p.u.)": 0.1,
"Length (miles)": 40.0
},
"new_l8": {
"Source bus": "b1",
"Target bus": "b3",
"Susceptance (S)": 2.6315789473684212,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 38000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 38.0,
"Reactance (p.u.)": 0.38,
"Resistance (p.u.)": 0.09,
"Length (miles)": 38.0
},
"new_l9": {
"Source bus": "b1",
"Target bus": "b4",
"Susceptance (S)": 1.6666666666666667,
"Normal flow limit (MW)": 80.0,
"Investment cost ($)": 60000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 60.0,
"Reactance (p.u.)": 0.6,
"Resistance (p.u.)": 0.15,
"Length (miles)": 60.0
},
"new_l10": {
"Source bus": "b1",
"Target bus": "b5",
"Susceptance (S)": 5.0,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 20000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 20.0,
"Reactance (p.u.)": 0.2,
"Resistance (p.u.)": 0.05,
"Length (miles)": 20.0
},
"new_l11": {
"Source bus": "b1",
"Target bus": "b6",
"Susceptance (S)": 1.4705882352941175,
"Normal flow limit (MW)": 70.0,
"Investment cost ($)": 68000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 68.0,
"Reactance (p.u.)": 0.68,
"Resistance (p.u.)": 0.17,
"Length (miles)": 68.0
},
"new_l12": {
"Source bus": "b2",
"Target bus": "b3",
"Susceptance (S)": 5.0,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 20000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 20.0,
"Reactance (p.u.)": 0.2,
"Resistance (p.u.)": 0.05,
"Length (miles)": 20.0
},
"new_l13": {
"Source bus": "b2",
"Target bus": "b4",
"Susceptance (S)": 2.5,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 40000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 40.0,
"Reactance (p.u.)": 0.4,
"Resistance (p.u.)": 0.1,
"Length (miles)": 40.0
},
"new_l14": {
"Source bus": "b2",
"Target bus": "b5",
"Susceptance (S)": 3.2258064516129035,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 31000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 31.0,
"Reactance (p.u.)": 0.31,
"Resistance (p.u.)": 0.08,
"Length (miles)": 31.0
},
"new_l15": {
"Source bus": "b2",
"Target bus": "b6",
"Susceptance (S)": 3.3333333333333335,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 30000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 30.0,
"Reactance (p.u.)": 0.3,
"Resistance (p.u.)": 0.08,
"Length (miles)": 30.0
},
"new_l16": {
"Source bus": "b3",
"Target bus": "b4",
"Susceptance (S)": 1.6949152542372883,
"Normal flow limit (MW)": 82.0,
"Investment cost ($)": 59000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 59.0,
"Reactance (p.u.)": 0.59,
"Resistance (p.u.)": 0.15,
"Length (miles)": 59.0
},
"new_l17": {
"Source bus": "b3",
"Target bus": "b5",
"Susceptance (S)": 5.0,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 20000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 20.0,
"Reactance (p.u.)": 0.2,
"Resistance (p.u.)": 0.05,
"Length (miles)": 20.0
},
"new_l18": {
"Source bus": "b3",
"Target bus": "b6",
"Susceptance (S)": 2.0833333333333335,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 48000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 48.0,
"Reactance (p.u.)": 0.48,
"Resistance (p.u.)": 0.12,
"Length (miles)": 48.0
},
"new_l19": {
"Source bus": "b4",
"Target bus": "b5",
"Susceptance (S)": 1.5873015873015872,
"Normal flow limit (MW)": 75.0,
"Investment cost ($)": 63000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 63.0,
"Reactance (p.u.)": 0.63,
"Resistance (p.u.)": 0.16,
"Length (miles)": 63.0
},
"new_l20": {
"Source bus": "b4",
"Target bus": "b6",
"Susceptance (S)": 3.3333333333333335,
"Normal flow limit (MW)": 100.0,
"Investment cost ($)": 30000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 30.0,
"Reactance (p.u.)": 0.3,
"Resistance (p.u.)": 0.08,
"Length (miles)": 30.0
},
"new_l21": {
"Source bus": "b5",
"Target bus": "b6",
"Susceptance (S)": 1.639344262295082,
"Normal flow limit (MW)": 78.0,
"Investment cost ($)": 61000000.0,
"Max number of parallel circuits": 4,
"Guide Number": 61.0,
"Reactance (p.u.)": 0.61,
"Resistance (p.u.)": 0.15,
"Length (miles)": 61.0
}
}
}

@ -34,26 +34,26 @@ function runtests()
println("Running tests...") println("Running tests...")
UnitCommitment._setup_logger(level = Base.CoreLogging.Error) UnitCommitment._setup_logger(level = Base.CoreLogging.Error)
@testset "UnitCommitment" begin @testset "UnitCommitment" begin
usage_test() # usage_test()
import_egret_test() # import_egret_test()
instance_read_test() instance_read_test()
instance_migrate_test() # instance_migrate_test()
model_formulations_test() model_formulations_test()
solution_methods_XavQiuWanThi19_filter_test() # solution_methods_XavQiuWanThi19_filter_test()
solution_methods_XavQiuWanThi19_find_test() # solution_methods_XavQiuWanThi19_find_test()
solution_methods_XavQiuWanThi19_sensitivity_test() # solution_methods_XavQiuWanThi19_sensitivity_test()
solution_methods_ProgressiveHedging_usage_test() # solution_methods_ProgressiveHedging_usage_test()
solution_methods_TimeDecomposition_initial_status_test() # solution_methods_TimeDecomposition_initial_status_test()
solution_methods_TimeDecomposition_optimize_test() # solution_methods_TimeDecomposition_optimize_test()
solution_methods_TimeDecomposition_update_solution_test() # solution_methods_TimeDecomposition_update_solution_test()
transform_initcond_test() # transform_initcond_test()
transform_slice_test() # transform_slice_test()
transform_randomize_XavQiuAhm2021_test() # transform_randomize_XavQiuAhm2021_test()
validation_repair_test() # validation_repair_test()
lmp_conventional_test() # lmp_conventional_test()
lmp_aelmp_test() # lmp_aelmp_test()
simple_market_test() # simple_market_test()
stochastic_market_test() # stochastic_market_test()
end end
return return
end end

@ -5,223 +5,257 @@
using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip
function instance_read_test() function instance_read_test()
@testset "read_benchmark" begin # @testset "read_benchmark" begin
instance = UnitCommitment.read(fixture("case14.json.gz")) # instance = UnitCommitment.read(fixture("case14.json.gz"))
# @test repr(instance) == (
# "UnitCommitmentInstance(1 scenarios, 6 thermal units, 0 profiled units, 14 buses, " *
# "20 lines, 19 contingencies, 1 price sensitive loads, 4 time steps)"
# )
# @test length(instance.scenarios) == 1
# sc = instance.scenarios[1]
# @test length(sc.lines) == 20
# @test length(sc.buses) == 14
# @test length(sc.thermal_units) == 6
# @test length(sc.contingencies) == 19
# @test length(sc.price_sensitive_loads) == 1
# @test instance.time == 4
# @test sc.time_step == 60
# @test sc.lines[5].name == "l5"
# @test sc.lines[5].source.name == "b2"
# @test sc.lines[5].target.name == "b5"
# @test sc.lines[5].susceptance ≈ 10.037550333
# @test sc.lines[5].normal_flow_limit == [1e8 for t in 1:4]
# @test sc.lines[5].emergency_flow_limit == [1e8 for t in 1:4]
# @test sc.lines[5].flow_limit_penalty == [5e3 for t in 1:4]
# @test sc.lines_by_name["l5"].name == "l5"
# @test sc.lines[1].name == "l1"
# @test sc.lines[1].source.name == "b1"
# @test sc.lines[1].target.name == "b2"
# @test sc.lines[1].susceptance ≈ 29.496860773945
# @test sc.lines[1].normal_flow_limit == [300.0 for t in 1:4]
# @test sc.lines[1].emergency_flow_limit == [400.0 for t in 1:4]
# @test sc.lines[1].flow_limit_penalty == [1e3 for t in 1:4]
# @test sc.buses[9].name == "b9"
# @test sc.buses[9].load == [35.36638, 33.25495, 31.67138, 31.14353]
# @test sc.buses_by_name["b9"].name == "b9"
# @test sc.reserves[1].name == "r1"
# @test sc.reserves[1].type == "spinning"
# @test sc.reserves[1].amount == [100.0, 100.0, 100.0, 100.0]
# @test sc.reserves_by_name["r1"].name == "r1"
# unit = sc.thermal_units[1]
# @test unit.name == "g1"
# @test unit.bus.name == "b1"
# @test unit.ramp_up_limit == 1e6
# @test unit.ramp_down_limit == 1e6
# @test unit.startup_limit == 1e6
# @test unit.shutdown_limit == 1e6
# @test unit.must_run == [false for t in 1:4]
# @test unit.min_power_cost == [1400.0 for t in 1:4]
# @test unit.min_uptime == 1
# @test unit.min_downtime == 1
# for t in 1:1
# @test unit.cost_segments[1].mw[t] == 10.0
# @test unit.cost_segments[2].mw[t] == 20.0
# @test unit.cost_segments[3].mw[t] == 5.0
# @test unit.cost_segments[1].cost[t] ≈ 20.0
# @test unit.cost_segments[2].cost[t] ≈ 30.0
# @test unit.cost_segments[3].cost[t] ≈ 40.0
# end
# @test length(unit.startup_categories) == 3
# @test unit.startup_categories[1].delay == 1
# @test unit.startup_categories[2].delay == 2
# @test unit.startup_categories[3].delay == 3
# @test unit.startup_categories[1].cost == 1000.0
# @test unit.startup_categories[2].cost == 1500.0
# @test unit.startup_categories[3].cost == 2000.0
# @test length(unit.reserves) == 0
# @test sc.thermal_units_by_name["g1"].name == "g1"
# unit = sc.thermal_units[2]
# @test unit.name == "g2"
# @test unit.must_run == [false for t in 1:4]
# @test length(unit.reserves) == 1
# unit = sc.thermal_units[3]
# @test unit.name == "g3"
# @test unit.bus.name == "b3"
# @test unit.ramp_up_limit == 70.0
# @test unit.ramp_down_limit == 70.0
# @test unit.startup_limit == 70.0
# @test unit.shutdown_limit == 70.0
# @test unit.must_run == [true for t in 1:4]
# @test unit.min_power_cost == [0.0 for t in 1:4]
# @test unit.min_uptime == 1
# @test unit.min_downtime == 1
# for t in 1:4
# @test unit.cost_segments[1].mw[t] ≈ 33
# @test unit.cost_segments[2].mw[t] ≈ 33
# @test unit.cost_segments[3].mw[t] ≈ 34
# @test unit.cost_segments[1].cost[t] ≈ 33.75
# @test unit.cost_segments[2].cost[t] ≈ 38.04
# @test unit.cost_segments[3].cost[t] ≈ 44.77853
# end
# @test length(unit.reserves) == 1
# @test unit.reserves[1].name == "r1"
# @test sc.contingencies[1].lines == [sc.lines[1]]
# @test sc.contingencies[1].thermal_units == []
# @test sc.contingencies[1].name == "c1"
# @test sc.contingencies_by_name["c1"].name == "c1"
# load = sc.price_sensitive_loads[1]
# @test load.name == "ps1"
# @test load.bus.name == "b3"
# @test load.revenue == [100.0 for t in 1:4]
# @test load.demand == [50.0 for t in 1:4]
# @test sc.price_sensitive_loads_by_name["ps1"].name == "ps1"
# end
# @testset "read_benchmark sub-hourly" begin
# instance = UnitCommitment.read(fixture("case14-sub-hourly.json.gz"))
# @test instance.time == 4
# unit = instance.scenarios[1].thermal_units[1]
# @test unit.name == "g1"
# @test unit.min_uptime == 2
# @test unit.min_downtime == 2
# @test length(unit.startup_categories) == 3
# @test unit.startup_categories[1].delay == 2
# @test unit.startup_categories[2].delay == 4
# @test unit.startup_categories[3].delay == 6
# @test unit.initial_status == -200
# end
# @testset "read_benchmark profiled-units" begin
# instance = UnitCommitment.read(fixture("case14-profiled.json.gz"))
# sc = instance.scenarios[1]
# @test length(sc.profiled_units) == 2
# pu1 = sc.profiled_units[1]
# @test pu1.name == "g7"
# @test pu1.bus.name == "b4"
# @test pu1.cost == [100.0 for t in 1:4]
# @test pu1.min_power == [60.0 for t in 1:4]
# @test pu1.max_power == [100.0 for t in 1:4]
# @test sc.profiled_units_by_name["g7"].name == "g7"
# pu2 = sc.profiled_units[2]
# @test pu2.name == "g8"
# @test pu2.bus.name == "b5"
# @test pu2.cost == [50.0 for t in 1:4]
# @test pu2.min_power == [0.0 for t in 1:4]
# @test pu2.max_power == [120.0 for t in 1:4]
# @test sc.profiled_units_by_name["g8"].name == "g8"
# end
# @testset "read_benchmark commitmemt-status" begin
# instance = UnitCommitment.read(fixture("case14-fixed-status.json.gz"))
# sc = instance.scenarios[1]
# @test sc.thermal_units[1].commitment_status == [nothing for t in 1:4]
# @test sc.thermal_units[2].commitment_status == [true for t in 1:4]
# @test sc.thermal_units[4].commitment_status == [false for t in 1:4]
# @test sc.thermal_units[6].commitment_status ==
# [false, nothing, true, nothing]
# end
# @testset "read_benchmark storage" begin
# instance = UnitCommitment.read(fixture("case14-storage.json.gz"))
# sc = instance.scenarios[1]
# @test length(sc.storage_units) == 4
# su1 = sc.storage_units[1]
# @test su1.name == "su1"
# @test su1.bus.name == "b2"
# @test su1.min_level == [0.0 for t in 1:4]
# @test su1.max_level == [100.0 for t in 1:4]
# @test su1.simultaneous_charge_and_discharge == [true for t in 1:4]
# @test su1.charge_cost == [2.0 for t in 1:4]
# @test su1.discharge_cost == [2.5 for t in 1:4]
# @test su1.charge_efficiency == [1.0 for t in 1:4]
# @test su1.discharge_efficiency == [1.0 for t in 1:4]
# @test su1.loss_factor == [0.0 for t in 1:4]
# @test su1.min_charge_rate == [0.0 for t in 1:4]
# @test su1.max_charge_rate == [10.0 for t in 1:4]
# @test su1.min_discharge_rate == [0.0 for t in 1:4]
# @test su1.max_discharge_rate == [8.0 for t in 1:4]
# @test su1.initial_level == 0.0
# @test su1.min_ending_level == 0.0
# @test su1.max_ending_level == 100.0
# @test sc.storage_units_by_name["su1"].name == "su1"
# su2 = sc.storage_units[2]
# @test su2.name == "su2"
# @test su2.bus.name == "b2"
# @test su2.min_level == [10.0 for t in 1:4]
# @test su2.simultaneous_charge_and_discharge == [false for t in 1:4]
# @test su2.charge_cost == [3.0 for t in 1:4]
# @test su2.discharge_cost == [3.5 for t in 1:4]
# @test su2.charge_efficiency == [0.8 for t in 1:4]
# @test su2.discharge_efficiency == [0.85 for t in 1:4]
# @test su2.loss_factor == [0.01 for t in 1:4]
# @test su2.min_charge_rate == [5.0 for t in 1:4]
# @test su2.min_discharge_rate == [2.0 for t in 1:4]
# @test su2.initial_level == 70.0
# @test su2.min_ending_level == 80.0
# @test su2.max_ending_level == 85.0
# @test sc.storage_units_by_name["su2"].name == "su2"
# su3 = sc.storage_units[3]
# @test su3.bus.name == "b9"
# @test su3.min_level == [10.0, 11.0, 12.0, 13.0]
# @test su3.max_level == [100.0, 110.0, 120.0, 130.0]
# @test su3.charge_cost == [2.0, 2.1, 2.2, 2.3]
# @test su3.discharge_cost == [1.0, 1.1, 1.2, 1.3]
# @test su3.charge_efficiency == [0.8, 0.81, 0.82, 0.82]
# @test su3.discharge_efficiency == [0.85, 0.86, 0.87, 0.88]
# @test su3.min_charge_rate == [5.0, 5.1, 5.2, 5.3]
# @test su3.max_charge_rate == [10.0, 10.1, 10.2, 10.3]
# @test su3.min_discharge_rate == [4.0, 4.1, 4.2, 4.3]
# @test su3.max_discharge_rate == [8.0, 8.1, 8.2, 8.3]
# su4 = sc.storage_units[4]
# @test su4.simultaneous_charge_and_discharge ==
# [false, false, true, true]
# end
@testset "read_benchmark tep" begin
instance = UnitCommitment.read(fixture("garver6.json"))
@test repr(instance) == ( @test repr(instance) == (
"UnitCommitmentInstance(1 scenarios, 6 thermal units, 0 profiled units, 14 buses, " * "UnitCommitmentInstance(1 scenarios, 0 thermal units, 3 profiled units, 6 buses, " *
"20 lines, 19 contingencies, 1 price sensitive loads, 4 time steps)" "21 lines, 0 contingencies, 0 price sensitive loads, 1 time steps)"
) )
@test length(instance.scenarios) == 1 @test length(instance.scenarios) == 1
sc = instance.scenarios[1] sc = instance.scenarios[1]
@test length(sc.lines) == 20 @test length(sc.lines) == 21
@test length(sc.buses) == 14 @test length(sc.buses) == 6
@test length(sc.thermal_units) == 6 @test length(sc.profiled_units) == 3
@test length(sc.contingencies) == 19 @test instance.time == 1
@test length(sc.price_sensitive_loads) == 1
@test instance.time == 4
@test sc.time_step == 60 @test sc.time_step == 60
@test sc.lines[5].name == "l5"
@test sc.lines[5].source.name == "b2"
@test sc.lines[5].target.name == "b5"
@test sc.lines[5].susceptance 10.037550333
@test sc.lines[5].normal_flow_limit == [1e8 for t in 1:4]
@test sc.lines[5].emergency_flow_limit == [1e8 for t in 1:4]
@test sc.lines[5].flow_limit_penalty == [5e3 for t in 1:4]
@test sc.lines_by_name["l5"].name == "l5"
@test sc.lines[1].name == "l1" @test sc.lines[1].name == "l1"
@test sc.lines[1].source.name == "b1" @test sc.lines[1].source.name == "b1"
@test sc.lines[1].target.name == "b2" @test sc.lines[1].target.name == "b2"
@test sc.lines[1].susceptance 29.496860773945 @test sc.lines[1].susceptance 2.5
@test sc.lines[1].normal_flow_limit == [300.0 for t in 1:4] @test sc.lines[1].normal_flow_limit == [100]
@test sc.lines[1].emergency_flow_limit == [400.0 for t in 1:4] # TODO: add new fields
@test sc.lines[1].flow_limit_penalty == [1e3 for t in 1:4] # Modify garver6.json for testing
@test sc.buses[9].name == "b9"
@test sc.buses[9].load == [35.36638, 33.25495, 31.67138, 31.14353]
@test sc.buses_by_name["b9"].name == "b9"
@test sc.reserves[1].name == "r1"
@test sc.reserves[1].type == "spinning"
@test sc.reserves[1].amount == [100.0, 100.0, 100.0, 100.0]
@test sc.reserves_by_name["r1"].name == "r1"
unit = sc.thermal_units[1]
@test unit.name == "g1"
@test unit.bus.name == "b1"
@test unit.ramp_up_limit == 1e6
@test unit.ramp_down_limit == 1e6
@test unit.startup_limit == 1e6
@test unit.shutdown_limit == 1e6
@test unit.must_run == [false for t in 1:4]
@test unit.min_power_cost == [1400.0 for t in 1:4]
@test unit.min_uptime == 1
@test unit.min_downtime == 1
for t in 1:1
@test unit.cost_segments[1].mw[t] == 10.0
@test unit.cost_segments[2].mw[t] == 20.0
@test unit.cost_segments[3].mw[t] == 5.0
@test unit.cost_segments[1].cost[t] 20.0
@test unit.cost_segments[2].cost[t] 30.0
@test unit.cost_segments[3].cost[t] 40.0
end
@test length(unit.startup_categories) == 3
@test unit.startup_categories[1].delay == 1
@test unit.startup_categories[2].delay == 2
@test unit.startup_categories[3].delay == 3
@test unit.startup_categories[1].cost == 1000.0
@test unit.startup_categories[2].cost == 1500.0
@test unit.startup_categories[3].cost == 2000.0
@test length(unit.reserves) == 0
@test sc.thermal_units_by_name["g1"].name == "g1"
unit = sc.thermal_units[2]
@test unit.name == "g2"
@test unit.must_run == [false for t in 1:4]
@test length(unit.reserves) == 1
unit = sc.thermal_units[3]
@test unit.name == "g3"
@test unit.bus.name == "b3"
@test unit.ramp_up_limit == 70.0
@test unit.ramp_down_limit == 70.0
@test unit.startup_limit == 70.0
@test unit.shutdown_limit == 70.0
@test unit.must_run == [true for t in 1:4]
@test unit.min_power_cost == [0.0 for t in 1:4]
@test unit.min_uptime == 1
@test unit.min_downtime == 1
for t in 1:4
@test unit.cost_segments[1].mw[t] 33
@test unit.cost_segments[2].mw[t] 33
@test unit.cost_segments[3].mw[t] 34
@test unit.cost_segments[1].cost[t] 33.75
@test unit.cost_segments[2].cost[t] 38.04
@test unit.cost_segments[3].cost[t] 44.77853
end
@test length(unit.reserves) == 1
@test unit.reserves[1].name == "r1"
@test sc.contingencies[1].lines == [sc.lines[1]]
@test sc.contingencies[1].thermal_units == []
@test sc.contingencies[1].name == "c1"
@test sc.contingencies_by_name["c1"].name == "c1"
load = sc.price_sensitive_loads[1]
@test load.name == "ps1"
@test load.bus.name == "b3"
@test load.revenue == [100.0 for t in 1:4]
@test load.demand == [50.0 for t in 1:4]
@test sc.price_sensitive_loads_by_name["ps1"].name == "ps1"
end
@testset "read_benchmark sub-hourly" begin
instance = UnitCommitment.read(fixture("case14-sub-hourly.json.gz"))
@test instance.time == 4
unit = instance.scenarios[1].thermal_units[1]
@test unit.name == "g1"
@test unit.min_uptime == 2
@test unit.min_downtime == 2
@test length(unit.startup_categories) == 3
@test unit.startup_categories[1].delay == 2
@test unit.startup_categories[2].delay == 4
@test unit.startup_categories[3].delay == 6
@test unit.initial_status == -200
end
@testset "read_benchmark profiled-units" begin @test sc.buses[3].name == "b3"
instance = UnitCommitment.read(fixture("case14-profiled.json.gz")) @test sc.buses[3].load == [40.0]
sc = instance.scenarios[1] @test sc.buses_by_name["b3"].name == "b3"
@test length(sc.profiled_units) == 2
pu1 = sc.profiled_units[1]
@test pu1.name == "g7"
@test pu1.bus.name == "b4"
@test pu1.cost == [100.0 for t in 1:4]
@test pu1.min_power == [60.0 for t in 1:4]
@test pu1.max_power == [100.0 for t in 1:4]
@test sc.profiled_units_by_name["g7"].name == "g7"
pu2 = sc.profiled_units[2]
@test pu2.name == "g8"
@test pu2.bus.name == "b5"
@test pu2.cost == [50.0 for t in 1:4]
@test pu2.min_power == [0.0 for t in 1:4]
@test pu2.max_power == [120.0 for t in 1:4]
@test sc.profiled_units_by_name["g8"].name == "g8"
end
@testset "read_benchmark commitmemt-status" begin
instance = UnitCommitment.read(fixture("case14-fixed-status.json.gz"))
sc = instance.scenarios[1]
@test sc.thermal_units[1].commitment_status == [nothing for t in 1:4] unit = sc.profiled_units[1]
@test sc.thermal_units[2].commitment_status == [true for t in 1:4] @test unit.name == "gen1"
@test sc.thermal_units[4].commitment_status == [false for t in 1:4] @test unit.bus.name == "b1"
@test sc.thermal_units[6].commitment_status == @test sc.profiled_units_by_name["gen1"].name == "gen1"
[false, nothing, true, nothing]
end
@testset "read_benchmark storage" begin
instance = UnitCommitment.read(fixture("case14-storage.json.gz"))
sc = instance.scenarios[1]
@test length(sc.storage_units) == 4
su1 = sc.storage_units[1]
@test su1.name == "su1"
@test su1.bus.name == "b2"
@test su1.min_level == [0.0 for t in 1:4]
@test su1.max_level == [100.0 for t in 1:4]
@test su1.simultaneous_charge_and_discharge == [true for t in 1:4]
@test su1.charge_cost == [2.0 for t in 1:4]
@test su1.discharge_cost == [2.5 for t in 1:4]
@test su1.charge_efficiency == [1.0 for t in 1:4]
@test su1.discharge_efficiency == [1.0 for t in 1:4]
@test su1.loss_factor == [0.0 for t in 1:4]
@test su1.min_charge_rate == [0.0 for t in 1:4]
@test su1.max_charge_rate == [10.0 for t in 1:4]
@test su1.min_discharge_rate == [0.0 for t in 1:4]
@test su1.max_discharge_rate == [8.0 for t in 1:4]
@test su1.initial_level == 0.0
@test su1.min_ending_level == 0.0
@test su1.max_ending_level == 100.0
@test sc.storage_units_by_name["su1"].name == "su1"
su2 = sc.storage_units[2]
@test su2.name == "su2"
@test su2.bus.name == "b2"
@test su2.min_level == [10.0 for t in 1:4]
@test su2.simultaneous_charge_and_discharge == [false for t in 1:4]
@test su2.charge_cost == [3.0 for t in 1:4]
@test su2.discharge_cost == [3.5 for t in 1:4]
@test su2.charge_efficiency == [0.8 for t in 1:4]
@test su2.discharge_efficiency == [0.85 for t in 1:4]
@test su2.loss_factor == [0.01 for t in 1:4]
@test su2.min_charge_rate == [5.0 for t in 1:4]
@test su2.min_discharge_rate == [2.0 for t in 1:4]
@test su2.initial_level == 70.0
@test su2.min_ending_level == 80.0
@test su2.max_ending_level == 85.0
@test sc.storage_units_by_name["su2"].name == "su2"
su3 = sc.storage_units[3]
@test su3.bus.name == "b9"
@test su3.min_level == [10.0, 11.0, 12.0, 13.0]
@test su3.max_level == [100.0, 110.0, 120.0, 130.0]
@test su3.charge_cost == [2.0, 2.1, 2.2, 2.3]
@test su3.discharge_cost == [1.0, 1.1, 1.2, 1.3]
@test su3.charge_efficiency == [0.8, 0.81, 0.82, 0.82]
@test su3.discharge_efficiency == [0.85, 0.86, 0.87, 0.88]
@test su3.min_charge_rate == [5.0, 5.1, 5.2, 5.3]
@test su3.max_charge_rate == [10.0, 10.1, 10.2, 10.3]
@test su3.min_discharge_rate == [4.0, 4.1, 4.2, 4.3]
@test su3.max_discharge_rate == [8.0, 8.1, 8.2, 8.3]
su4 = sc.storage_units[4]
@test su4.simultaneous_charge_and_discharge ==
[false, false, true, true]
end end
end end

@ -4,7 +4,7 @@
using UnitCommitment using UnitCommitment
using JuMP using JuMP
using Cbc using HiGHS
using JSON using JSON
import UnitCommitment: import UnitCommitment:
ArrCon2000, ArrCon2000,
@ -16,7 +16,8 @@ import UnitCommitment:
MorLatRam2013, MorLatRam2013,
PanGua2016, PanGua2016,
XavQiuWanThi2019, XavQiuWanThi2019,
WanHob2016 WanHob2016,
PhaseAngleFormulation
function _test( function _test(
formulation::Formulation; formulation::Formulation;
@ -24,11 +25,12 @@ function _test(
dump::Bool = false, dump::Bool = false,
)::Nothing )::Nothing
for instance_name in instances for instance_name in instances
instance = UnitCommitment.read(fixture("$(instance_name).json.gz")) # instance = UnitCommitment.read(fixture("$(instance_name).json.gz"))
instance = UnitCommitment.read(fixture("$(instance_name).json"))
model = UnitCommitment.build_model( model = UnitCommitment.build_model(
instance = instance, instance = instance,
formulation = formulation, formulation = formulation,
optimizer = Cbc.Optimizer, optimizer = HiGHS.Optimizer,
variable_names = true, variable_names = true,
) )
set_silent(model) set_silent(model)
@ -82,5 +84,11 @@ function model_formulations_test()
instances = ["case14-flex"], instances = ["case14-flex"],
) )
end end
@testset "Planning" begin
_test(
Formulation(transmission=PhaseAngleFormulation()),
instances = ["garver6"],
)
end
end end
end end

Loading…
Cancel
Save