You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
96 lines
3.9 KiB
96 lines
3.9 KiB
# 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 solution(model::JuMP.Model)::OrderedDict
|
|
instance, T = model[:instance], model[:instance].time
|
|
function timeseries(vars, collection)
|
|
return OrderedDict(
|
|
b.name => [round(value(vars[b.name, t]), digits = 5) for t in 1:T]
|
|
for b in collection
|
|
)
|
|
end
|
|
function production_cost(g)
|
|
return [
|
|
value(model[:is_on][g.name, t]) * g.min_power_cost[t] + sum(
|
|
Float64[
|
|
value(model[:segprod][g.name, t, k]) *
|
|
g.cost_segments[k].cost[t] for
|
|
k in 1:length(g.cost_segments)
|
|
],
|
|
) for t in 1:T
|
|
]
|
|
end
|
|
function production(g)
|
|
return [
|
|
value(model[:is_on][g.name, t]) * g.min_power[t] + sum(
|
|
Float64[
|
|
value(model[:segprod][g.name, t, k]) for
|
|
k in 1:length(g.cost_segments)
|
|
],
|
|
) for t in 1:T
|
|
]
|
|
end
|
|
function startup_cost(g)
|
|
S = length(g.startup_categories)
|
|
return [
|
|
sum(
|
|
g.startup_categories[s].cost *
|
|
value(model[:startup][g.name, t, s]) for s in 1:S
|
|
) for t in 1:T
|
|
]
|
|
end
|
|
sol = OrderedDict()
|
|
sol["Production (MW)"] =
|
|
OrderedDict(g.name => production(g) for g in instance.units)
|
|
sol["Production cost (\$)"] =
|
|
OrderedDict(g.name => production_cost(g) for g in instance.units)
|
|
sol["Startup cost (\$)"] =
|
|
OrderedDict(g.name => startup_cost(g) for g in instance.units)
|
|
sol["Is on"] = timeseries(model[:is_on], instance.units)
|
|
sol["Switch on"] = timeseries(model[:switch_on], instance.units)
|
|
sol["Switch off"] = timeseries(model[:switch_off], instance.units)
|
|
if instance.reserves.upflexiramp != zeros(T) ||
|
|
instance.reserves.dwflexiramp != zeros(T)
|
|
# Report flexiramp solutions only if either of the up-flexiramp and
|
|
# down-flexiramp requirements is not a default array of zeros
|
|
sol["Up-flexiramp (MW)"] =
|
|
timeseries(model[:upflexiramp], instance.units)
|
|
sol["Up-flexiramp shortfall (MW)"] = OrderedDict(
|
|
t =>
|
|
(instance.flexiramp_shortfall_penalty[t] >= 0) ?
|
|
round(value(model[:upflexiramp_shortfall][t]), digits = 5) :
|
|
0.0 for t in 1:instance.time
|
|
)
|
|
sol["Down-flexiramp (MW)"] =
|
|
timeseries(model[:dwflexiramp], instance.units)
|
|
sol["Down-flexiramp shortfall (MW)"] = OrderedDict(
|
|
t =>
|
|
(instance.flexiramp_shortfall_penalty[t] >= 0) ?
|
|
round(value(model[:dwflexiramp_shortfall][t]), digits = 5) :
|
|
0.0 for t in 1:instance.time
|
|
)
|
|
else
|
|
# Report spinning reserve solutions only if both up-flexiramp and
|
|
# down-flexiramp requirements are arrays of zeros.
|
|
sol["Reserve (MW)"] = timeseries(model[:reserve], instance.units)
|
|
sol["Reserve shortfall (MW)"] = OrderedDict(
|
|
t =>
|
|
(instance.shortfall_penalty[t] >= 0) ?
|
|
round(value(model[:reserve_shortfall][t]), digits = 5) :
|
|
0.0 for t in 1:instance.time
|
|
)
|
|
end
|
|
sol["Net injection (MW)"] =
|
|
timeseries(model[:net_injection], instance.buses)
|
|
sol["Load curtail (MW)"] = timeseries(model[:curtail], instance.buses)
|
|
if !isempty(instance.lines)
|
|
sol["Line overflow (MW)"] = timeseries(model[:overflow], instance.lines)
|
|
end
|
|
if !isempty(instance.price_sensitive_loads)
|
|
sol["Price-sensitive loads (MW)"] =
|
|
timeseries(model[:loads], instance.price_sensitive_loads)
|
|
end
|
|
return sol
|
|
end
|