|
|
@ -97,12 +97,14 @@ end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function Base.show(io::IO, instance::UnitCommitmentInstance)
|
|
|
|
function Base.show(io::IO, instance::UnitCommitmentInstance)
|
|
|
|
print(io, "UnitCommitmentInstance with ")
|
|
|
|
print(io, "UnitCommitmentInstance(")
|
|
|
|
print(io, "$(length(instance.units)) units, ")
|
|
|
|
print(io, "$(length(instance.units)) units, ")
|
|
|
|
print(io, "$(length(instance.buses)) buses, ")
|
|
|
|
print(io, "$(length(instance.buses)) buses, ")
|
|
|
|
print(io, "$(length(instance.lines)) lines, ")
|
|
|
|
print(io, "$(length(instance.lines)) lines, ")
|
|
|
|
print(io, "$(length(instance.contingencies)) contingencies, ")
|
|
|
|
print(io, "$(length(instance.contingencies)) contingencies, ")
|
|
|
|
print(io, "$(length(instance.price_sensitive_loads)) price sensitive loads")
|
|
|
|
print(io, "$(length(instance.price_sensitive_loads)) price sensitive loads, ")
|
|
|
|
|
|
|
|
print(io, "$(instance.time) time steps")
|
|
|
|
|
|
|
|
print(io, ")")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -131,7 +133,21 @@ function from_json(json; fix=true)
|
|
|
|
contingencies = Contingency[]
|
|
|
|
contingencies = Contingency[]
|
|
|
|
lines = TransmissionLine[]
|
|
|
|
lines = TransmissionLine[]
|
|
|
|
loads = PriceSensitiveLoad[]
|
|
|
|
loads = PriceSensitiveLoad[]
|
|
|
|
T = json["Parameters"]["Time (h)"]
|
|
|
|
|
|
|
|
|
|
|
|
function scalar(x; default=nothing)
|
|
|
|
|
|
|
|
x !== nothing || return default
|
|
|
|
|
|
|
|
x
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
time_horizon = json["Parameters"]["Time (h)"]
|
|
|
|
|
|
|
|
if time_horizon === nothing
|
|
|
|
|
|
|
|
time_horizon = json["Parameters"]["Time horizon (h)"]
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
time_horizon !== nothing || error("Missing required parameter: Time horizon (h)")
|
|
|
|
|
|
|
|
time_step = scalar(json["Parameters"]["Time step (min)"], default=60)
|
|
|
|
|
|
|
|
(60 % time_step == 0) || error("Time step $time_step is not a divisor of 60")
|
|
|
|
|
|
|
|
time_multiplier = 60 ÷ time_step
|
|
|
|
|
|
|
|
T = time_horizon * time_multiplier
|
|
|
|
|
|
|
|
|
|
|
|
name_to_bus = Dict{String, Bus}()
|
|
|
|
name_to_bus = Dict{String, Bus}()
|
|
|
|
name_to_line = Dict{String, TransmissionLine}()
|
|
|
|
name_to_line = Dict{String, TransmissionLine}()
|
|
|
@ -143,11 +159,6 @@ function from_json(json; fix=true)
|
|
|
|
return x
|
|
|
|
return x
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
function scalar(x; default=nothing)
|
|
|
|
|
|
|
|
x !== nothing || return default
|
|
|
|
|
|
|
|
x
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Read parameters
|
|
|
|
# Read parameters
|
|
|
|
power_balance_penalty = timeseries(json["Parameters"]["Power balance penalty (\$/MW)"],
|
|
|
|
power_balance_penalty = timeseries(json["Parameters"]["Power balance penalty (\$/MW)"],
|
|
|
|
default=[1000.0 for t in 1:T])
|
|
|
|
default=[1000.0 for t in 1:T])
|
|
|
@ -187,8 +198,13 @@ function from_json(json; fix=true)
|
|
|
|
startup_costs = scalar(dict["Startup costs (\$)"], default=[0.])
|
|
|
|
startup_costs = scalar(dict["Startup costs (\$)"], default=[0.])
|
|
|
|
startup_categories = StartupCategory[]
|
|
|
|
startup_categories = StartupCategory[]
|
|
|
|
for k in 1:length(startup_delays)
|
|
|
|
for k in 1:length(startup_delays)
|
|
|
|
push!(startup_categories, StartupCategory(startup_delays[k],
|
|
|
|
push!(
|
|
|
|
startup_costs[k]))
|
|
|
|
startup_categories,
|
|
|
|
|
|
|
|
StartupCategory(
|
|
|
|
|
|
|
|
startup_delays[k] .* time_multiplier,
|
|
|
|
|
|
|
|
startup_costs[k],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
# Read and validate initial conditions
|
|
|
|
# Read and validate initial conditions
|
|
|
@ -202,26 +218,31 @@ function from_json(json; fix=true)
|
|
|
|
if initial_status < 0 && initial_power > 1e-3
|
|
|
|
if initial_status < 0 && initial_power > 1e-3
|
|
|
|
error("unit $unit_name has invalid initial power")
|
|
|
|
error("unit $unit_name has invalid initial power")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
initial_status *= time_multiplier
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
unit = Unit(unit_name,
|
|
|
|
unit = Unit(
|
|
|
|
|
|
|
|
unit_name,
|
|
|
|
bus,
|
|
|
|
bus,
|
|
|
|
max_power,
|
|
|
|
max_power,
|
|
|
|
min_power,
|
|
|
|
min_power,
|
|
|
|
timeseries(dict["Must run?"], default=[false for t in 1:T]),
|
|
|
|
timeseries(dict["Must run?"], default=[false for t in 1:T]),
|
|
|
|
min_power_cost,
|
|
|
|
min_power_cost,
|
|
|
|
segments,
|
|
|
|
segments,
|
|
|
|
scalar(dict["Minimum uptime (h)"], default=1),
|
|
|
|
scalar(dict["Minimum uptime (h)"], default=1) * time_multiplier,
|
|
|
|
scalar(dict["Minimum downtime (h)"], default=1),
|
|
|
|
scalar(dict["Minimum downtime (h)"], default=1) * time_multiplier,
|
|
|
|
scalar(dict["Ramp up limit (MW)"], default=1e6),
|
|
|
|
scalar(dict["Ramp up limit (MW)"], default=1e6),
|
|
|
|
scalar(dict["Ramp down limit (MW)"], default=1e6),
|
|
|
|
scalar(dict["Ramp down limit (MW)"], default=1e6),
|
|
|
|
scalar(dict["Startup limit (MW)"], default=1e6),
|
|
|
|
scalar(dict["Startup limit (MW)"], default=1e6),
|
|
|
|
scalar(dict["Shutdown limit (MW)"], default=1e6),
|
|
|
|
scalar(dict["Shutdown limit (MW)"], default=1e6),
|
|
|
|
initial_status,
|
|
|
|
initial_status,
|
|
|
|
initial_power,
|
|
|
|
initial_power,
|
|
|
|
timeseries(dict["Provides spinning reserves?"],
|
|
|
|
timeseries(
|
|
|
|
default=[true for t in 1:T]),
|
|
|
|
dict["Provides spinning reserves?"],
|
|
|
|
startup_categories)
|
|
|
|
default=[true for t in 1:T],
|
|
|
|
|
|
|
|
),
|
|
|
|
|
|
|
|
startup_categories,
|
|
|
|
|
|
|
|
)
|
|
|
|
push!(bus.units, unit)
|
|
|
|
push!(bus.units, unit)
|
|
|
|
name_to_unit[unit_name] = unit
|
|
|
|
name_to_unit[unit_name] = unit
|
|
|
|
push!(units, unit)
|
|
|
|
push!(units, unit)
|
|
|
|