diff --git a/benchmark/.DS_Store b/benchmark/.DS_Store new file mode 100644 index 0000000..e22e565 Binary files /dev/null and b/benchmark/.DS_Store differ diff --git a/benchmark/run.jl b/benchmark/run.jl index 9c2a662..aff8b3b 100644 --- a/benchmark/run.jl +++ b/benchmark/run.jl @@ -129,19 +129,15 @@ formulations = Dict( const gap_limit = parse(Float64, args["--gap"]) const time_limit = parse(Float64, args["--time-limit"]) methods = Dict( - "default" => XavQiuWanThi2019.Method( - time_limit = time_limit, - gap_limit = gap_limit, - ), + "default" => + XavQiuWanThi2019.Method(time_limit = time_limit, gap_limit = gap_limit), ) # MIP solvers # ----------------------------------------------------------------------------- optimizers = Dict( - "gurobi" => optimizer_with_attributes( - Gurobi.Optimizer, - "Threads" => Threads.nthreads(), - ), + "gurobi" => + optimizer_with_attributes(Gurobi.Optimizer, "Threads" => Threads.nthreads()), ) # Parse command line arguments diff --git a/src/import/egret.jl b/src/import/egret.jl index d11bdea..97c7438 100644 --- a/src/import/egret.jl +++ b/src/import/egret.jl @@ -41,7 +41,7 @@ function read_egret_solution(path::String)::OrderedDict startup_cost[gen_name] = zeros(T) production_cost[gen_name] = zeros(T) if "commitment_cost" in keys(gen_dict) - for t in 1:T + for t = 1:T x = gen_dict["commitment"]["values"][t] commitment_cost = gen_dict["commitment_cost"]["values"][t] prod_above_cost = gen_dict["production_cost"]["values"][t] diff --git a/src/instance/read.jl b/src/instance/read.jl index 09c2da4..19249ed 100644 --- a/src/instance/read.jl +++ b/src/instance/read.jl @@ -23,10 +23,7 @@ Example import UnitCommitment instance = UnitCommitment.read_benchmark("matpower/case3375wp/2017-02-01") """ -function read_benchmark( - name::AbstractString; - quiet::Bool = false, -)::UnitCommitmentInstance +function read_benchmark(name::AbstractString; quiet::Bool = false)::UnitCommitmentInstance basedir = dirname(@__FILE__) filename = "$basedir/../../instances/$name.json.gz" url = "$INSTANCES_URL/$name.json.gz" @@ -65,9 +62,7 @@ function read(path::AbstractString)::UnitCommitmentInstance end function _read(file::IO)::UnitCommitmentInstance - return _from_json( - JSON.parse(file, dicttype = () -> DefaultOrderedDict(nothing)), - ) + return _from_json(JSON.parse(file, dicttype = () -> DefaultOrderedDict(nothing))) end function _read_json(path::String)::OrderedDict @@ -97,8 +92,7 @@ function _from_json(json; repair = true) end time_horizon !== nothing || error("Missing 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") + (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 @@ -108,23 +102,23 @@ function _from_json(json; repair = true) function timeseries(x; default = nothing) x !== nothing || return default - x isa Array || return [x for t in 1:T] + x isa Array || return [x for t = 1:T] return x end # Read parameters power_balance_penalty = timeseries( json["Parameters"]["Power balance penalty (\$/MW)"], - default = [1000.0 for t in 1:T], + default = [1000.0 for t = 1:T], ) # Penalty price for shortage in meeting system-wide flexiramp requirements flexiramp_shortfall_penalty = timeseries( json["Parameters"]["Flexiramp penalty (\$/MW)"], - default = [500.0 for t in 1:T], + default = [500.0 for t = 1:T], ) shortfall_penalty = timeseries( json["Parameters"]["Reserve shortfall penalty (\$/MW)"], - default = [-1.0 for t in 1:T], + default = [-1.0 for t = 1:T], ) # Read buses @@ -146,17 +140,14 @@ function _from_json(json; repair = true) # Read production cost curve K = length(dict["Production cost curve (MW)"]) - curve_mw = hcat( - [timeseries(dict["Production cost curve (MW)"][k]) for k in 1:K]..., - ) - curve_cost = hcat( - [timeseries(dict["Production cost curve (\$)"][k]) for k in 1:K]..., - ) + curve_mw = hcat([timeseries(dict["Production cost curve (MW)"][k]) for k = 1:K]...) + curve_cost = + hcat([timeseries(dict["Production cost curve (\$)"][k]) for k = 1:K]...) min_power = curve_mw[:, 1] max_power = curve_mw[:, K] min_power_cost = curve_cost[:, 1] segments = CostSegment[] - for k in 2:K + for k = 2:K amount = curve_mw[:, k] - curve_mw[:, k-1] cost = (curve_cost[:, k] - curve_cost[:, k-1]) ./ amount replace!(cost, NaN => 0.0) @@ -167,13 +158,10 @@ function _from_json(json; repair = true) startup_delays = scalar(dict["Startup delays (h)"], default = [1]) startup_costs = scalar(dict["Startup costs (\$)"], default = [0.0]) startup_categories = StartupCategory[] - for k in 1:length(startup_delays) + for k = 1:length(startup_delays) push!( startup_categories, - StartupCategory( - startup_delays[k] .* time_multiplier, - startup_costs[k], - ), + StartupCategory(startup_delays[k] .* time_multiplier, startup_costs[k]), ) end @@ -186,8 +174,7 @@ function _from_json(json; repair = true) else initial_status !== nothing || error("unit $unit_name has initial power but no initial status") - initial_status != 0 || - error("unit $unit_name has invalid initial status") + initial_status != 0 || error("unit $unit_name has invalid initial status") if initial_status < 0 && initial_power > 1e-3 error("unit $unit_name has invalid initial power") end @@ -199,7 +186,7 @@ function _from_json(json; repair = true) bus, max_power, min_power, - timeseries(dict["Must run?"], default = [false for t in 1:T]), + timeseries(dict["Must run?"], default = [false for t = 1:T]), min_power_cost, segments, scalar(dict["Minimum uptime (h)"], default = 1) * time_multiplier, @@ -210,14 +197,8 @@ function _from_json(json; repair = true) scalar(dict["Shutdown limit (MW)"], default = 1e6), initial_status, initial_power, - timeseries( - dict["Provides spinning reserves?"], - default = [true for t in 1:T], - ), - timeseries( - dict["Provides flexible capacity?"], - default = [true for t in 1:T], - ), + timeseries(dict["Provides spinning reserves?"], default = [true for t = 1:T]), + timeseries(dict["Provides flexible capacity?"], default = [true for t = 1:T]), startup_categories, ) push!(bus.units, unit) @@ -230,14 +211,10 @@ function _from_json(json; repair = true) if "Reserves" in keys(json) reserves.spinning = timeseries(json["Reserves"]["Spinning (MW)"], default = zeros(T)) - reserves.upflexiramp = timeseries( - json["Reserves"]["Up-flexiramp (MW)"], - default = zeros(T), - ) - reserves.dwflexiramp = timeseries( - json["Reserves"]["Down-flexiramp (MW)"], - default = zeros(T), - ) + reserves.upflexiramp = + timeseries(json["Reserves"]["Up-flexiramp (MW)"], default = zeros(T)) + reserves.dwflexiramp = + timeseries(json["Reserves"]["Down-flexiramp (MW)"], default = zeros(T)) end # Read transmission lines @@ -250,17 +227,11 @@ function _from_json(json; repair = true) name_to_bus[dict["Target bus"]], scalar(dict["Reactance (ohms)"]), scalar(dict["Susceptance (S)"]), - timeseries( - dict["Normal flow limit (MW)"], - default = [1e8 for t in 1:T], - ), - timeseries( - dict["Emergency flow limit (MW)"], - default = [1e8 for t in 1:T], - ), + timeseries(dict["Normal flow limit (MW)"], default = [1e8 for t = 1:T]), + timeseries(dict["Emergency flow limit (MW)"], default = [1e8 for t = 1:T]), timeseries( dict["Flow limit penalty (\$/MW)"], - default = [5000.0 for t in 1:T], + default = [5000.0 for t = 1:T], ), ) name_to_line[line_name] = line @@ -274,12 +245,10 @@ function _from_json(json; repair = true) affected_units = Unit[] affected_lines = TransmissionLine[] if "Affected lines" in keys(dict) - affected_lines = - [name_to_line[l] for l in dict["Affected lines"]] + affected_lines = [name_to_line[l] for l in dict["Affected lines"]] end if "Affected units" in keys(dict) - affected_units = - [name_to_unit[u] for u in dict["Affected units"]] + affected_units = [name_to_unit[u] for u in dict["Affected units"]] end cont = Contingency(cont_name, affected_lines, affected_units) push!(contingencies, cont) diff --git a/src/instance/structs.jl b/src/instance/structs.jl index 8555316..03a5af4 100644 --- a/src/instance/structs.jl +++ b/src/instance/structs.jl @@ -96,10 +96,7 @@ function Base.show(io::IO, instance::UnitCommitmentInstance) print(io, "$(length(instance.buses)) buses, ") print(io, "$(length(instance.lines)) lines, ") 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, ")") return diff --git a/src/model/build.jl b/src/model/build.jl index de81600..5352b29 100644 --- a/src/model/build.jl +++ b/src/model/build.jl @@ -33,19 +33,15 @@ function build_model(; variable_names::Bool = false, )::JuMP.Model if formulation.ramping == WanHob2016.Ramping() && - instance.reserves.spinning >= ones(instance.time).*1e-6 - error( - "Spinning reserves are not supported by the WanHob2016 ramping formulation", - ) + instance.reserves.spinning >= ones(instance.time) .* 1e-6 + error("Spinning reserves are not supported by the WanHob2016 ramping formulation") end - + if formulation.ramping !== WanHob2016.Ramping() && ( - instance.reserves.upflexiramp >= ones(instance.time).*1e-6 || - instance.reserves.dwflexiramp >= ones(instance.time).*1e-6 + instance.reserves.upflexiramp >= ones(instance.time) .* 1e-6 || + instance.reserves.dwflexiramp >= ones(instance.time) .* 1e-6 ) - error( - "Flexiramp is supported only by the WanHob2016 ramping formulation", - ) + error("Flexiramp is supported only by the WanHob2016 ramping formulation") end @info "Building model..." diff --git a/src/model/formulations/ArrCon2000/ramp.jl b/src/model/formulations/ArrCon2000/ramp.jl index e9bb288..c8aed14 100644 --- a/src/model/formulations/ArrCon2000/ramp.jl +++ b/src/model/formulations/ArrCon2000/ramp.jl @@ -32,7 +32,7 @@ function _add_ramp_eqs!( switch_off = model[:switch_off] switch_on = model[:switch_on] - for t in 1:model[:instance].time + for t = 1:model[:instance].time # Ramp up limit if t == 1 if is_initially_on @@ -49,12 +49,8 @@ function _add_ramp_eqs!( max_prod_this_period = g.min_power[t] * is_on[gn, t] + prod_above[gn, t] + - ( - RESERVES_WHEN_START_UP || RESERVES_WHEN_RAMP_UP ? - reserve[gn, t] : 0.0 - ) - min_prod_last_period = - g.min_power[t-1] * is_on[gn, t-1] + prod_above[gn, t-1] + (RESERVES_WHEN_START_UP || RESERVES_WHEN_RAMP_UP ? reserve[gn, t] : 0.0) + min_prod_last_period = g.min_power[t-1] * is_on[gn, t-1] + prod_above[gn, t-1] # Equation (24) in Kneuven et al. (2020) eq_ramp_up[gn, t] = @constraint( @@ -81,11 +77,10 @@ function _add_ramp_eqs!( g.min_power[t-1] * is_on[gn, t-1] + prod_above[gn, t-1] + ( - RESERVES_WHEN_SHUT_DOWN || RESERVES_WHEN_RAMP_DOWN ? - reserve[gn, t-1] : 0.0 + RESERVES_WHEN_SHUT_DOWN || RESERVES_WHEN_RAMP_DOWN ? reserve[gn, t-1] : + 0.0 ) - min_prod_this_period = - g.min_power[t] * is_on[gn, t] + prod_above[gn, t] + min_prod_this_period = g.min_power[t] * is_on[gn, t] + prod_above[gn, t] # Equation (25) in Kneuven et al. (2020) eq_ramp_down[gn, t] = @constraint( diff --git a/src/model/formulations/CarArr2006/pwlcosts.jl b/src/model/formulations/CarArr2006/pwlcosts.jl index 2f13e9a..ad2e1c9 100644 --- a/src/model/formulations/CarArr2006/pwlcosts.jl +++ b/src/model/formulations/CarArr2006/pwlcosts.jl @@ -18,18 +18,16 @@ function _add_production_piecewise_linear_eqs!( prod_above = model[:prod_above] K = length(g.cost_segments) - for t in 1:model[:instance].time + for t = 1:model[:instance].time gn = g.name - for k in 1:K + for k = 1:K # Equation (45) in Kneuven et al. (2020) # NB: when reading instance, UnitCommitment.jl already calculates # difference between max power for segments k and k-1 so the # value of cost_segments[k].mw[t] is the max production *for # that segment* - eq_segprod_limit[gn, t, k] = @constraint( - model, - segprod[gn, t, k] <= g.cost_segments[k].mw[t] - ) + eq_segprod_limit[gn, t, k] = + @constraint(model, segprod[gn, t, k] <= g.cost_segments[k].mw[t]) # Also add this as an explicit upper bound on segprod to make the # solver's work a bit easier @@ -37,18 +35,12 @@ function _add_production_piecewise_linear_eqs!( # Definition of production # Equation (43) in Kneuven et al. (2020) - eq_prod_above_def[gn, t] = @constraint( - model, - prod_above[gn, t] == sum(segprod[gn, t, k] for k in 1:K) - ) + eq_prod_above_def[gn, t] = + @constraint(model, prod_above[gn, t] == sum(segprod[gn, t, k] for k = 1:K)) # Objective function # Equation (44) in Kneuven et al. (2020) - add_to_expression!( - model[:obj], - segprod[gn, t, k], - g.cost_segments[k].cost[t], - ) + add_to_expression!(model[:obj], segprod[gn, t, k], g.cost_segments[k].cost[t]) end end end diff --git a/src/model/formulations/DamKucRajAta2016/ramp.jl b/src/model/formulations/DamKucRajAta2016/ramp.jl index 9afd247..d4fa5f6 100644 --- a/src/model/formulations/DamKucRajAta2016/ramp.jl +++ b/src/model/formulations/DamKucRajAta2016/ramp.jl @@ -33,9 +33,8 @@ function _add_ramp_eqs!( switch_off = model[:switch_off] switch_on = model[:switch_on] - for t in 1:model[:instance].time - time_invariant = - (t > 1) ? (abs(g.min_power[t] - g.min_power[t-1]) < 1e-7) : true + for t = 1:model[:instance].time + time_invariant = (t > 1) ? (abs(g.min_power[t] - g.min_power[t-1]) < 1e-7) : true # if t > 1 && !time_invariant # @warn( @@ -48,10 +47,8 @@ function _add_ramp_eqs!( # end max_prod_this_period = - prod_above[gn, t] + ( - RESERVES_WHEN_START_UP || RESERVES_WHEN_RAMP_UP ? - reserve[gn, t] : 0.0 - ) + prod_above[gn, t] + + (RESERVES_WHEN_START_UP || RESERVES_WHEN_RAMP_UP ? reserve[gn, t] : 0.0) min_prod_last_period = 0.0 if t > 1 && time_invariant min_prod_last_period = prod_above[gn, t-1] @@ -61,8 +58,7 @@ function _add_ramp_eqs!( eq_str_ramp_up[gn, t] = @constraint( model, max_prod_this_period - min_prod_last_period <= - (SU - g.min_power[t] - RU) * switch_on[gn, t] + - RU * is_on[gn, t] + (SU - g.min_power[t] - RU) * switch_on[gn, t] + RU * is_on[gn, t] ) elseif (t == 1 && is_initially_on) || (t > 1 && !time_invariant) if t > 1 @@ -103,8 +99,7 @@ function _add_ramp_eqs!( eq_str_ramp_down[gn, t] = @constraint( model, max_prod_last_period - min_prod_this_period <= - (SD - g.min_power[t] - RD) * switch_off[gn, t] + - RD * on_last_period + (SD - g.min_power[t] - RD) * switch_off[gn, t] + RD * on_last_period ) elseif (t == 1 && is_initially_on) || (t > 1 && !time_invariant) # Add back in min power diff --git a/src/model/formulations/Gar1962/prod.jl b/src/model/formulations/Gar1962/prod.jl index e39a90e..a56e5da 100644 --- a/src/model/formulations/Gar1962/prod.jl +++ b/src/model/formulations/Gar1962/prod.jl @@ -9,8 +9,8 @@ function _add_production_vars!( )::Nothing prod_above = _init(model, :prod_above) segprod = _init(model, :segprod) - for t in 1:model[:instance].time - for k in 1:length(g.cost_segments) + for t = 1:model[:instance].time + for k = 1:length(g.cost_segments) segprod[g.name, t, k] = @variable(model, lower_bound = 0) end prod_above[g.name, t] = @variable(model, lower_bound = 0) @@ -28,7 +28,7 @@ function _add_production_limit_eqs!( prod_above = model[:prod_above] reserve = model[:reserve] gn = g.name - for t in 1:model[:instance].time + for t = 1:model[:instance].time # Objective function terms for production costs # Part of (69) of Kneuven et al. (2020) as C^R_g * u_g(t) term add_to_expression!(model[:obj], is_on[gn, t], g.min_power_cost[t]) diff --git a/src/model/formulations/Gar1962/pwlcosts.jl b/src/model/formulations/Gar1962/pwlcosts.jl index 3ac4871..c55d449 100644 --- a/src/model/formulations/Gar1962/pwlcosts.jl +++ b/src/model/formulations/Gar1962/pwlcosts.jl @@ -21,15 +21,13 @@ function _add_production_piecewise_linear_eqs!( is_on = model[:is_on] K = length(g.cost_segments) - for t in 1:model[:instance].time + for t = 1:model[:instance].time # Definition of production # Equation (43) in Kneuven et al. (2020) - eq_prod_above_def[gn, t] = @constraint( - model, - prod_above[gn, t] == sum(segprod[gn, t, k] for k in 1:K) - ) + eq_prod_above_def[gn, t] = + @constraint(model, prod_above[gn, t] == sum(segprod[gn, t, k] for k = 1:K)) - for k in 1:K + for k = 1:K # Equation (42) in Kneuven et al. (2020) # Without this, solvers will add a lot of implied bound cuts to # have this same effect. @@ -48,11 +46,7 @@ function _add_production_piecewise_linear_eqs!( # Objective function # Equation (44) in Kneuven et al. (2020) - add_to_expression!( - model[:obj], - segprod[gn, t, k], - g.cost_segments[k].cost[t], - ) + add_to_expression!(model[:obj], segprod[gn, t, k], g.cost_segments[k].cost[t]) end end return diff --git a/src/model/formulations/Gar1962/status.jl b/src/model/formulations/Gar1962/status.jl index 14c055f..c2b1a32 100644 --- a/src/model/formulations/Gar1962/status.jl +++ b/src/model/formulations/Gar1962/status.jl @@ -10,7 +10,7 @@ function _add_status_vars!( is_on = _init(model, :is_on) switch_on = _init(model, :switch_on) switch_off = _init(model, :switch_off) - for t in 1:model[:instance].time + for t = 1:model[:instance].time if g.must_run[t] is_on[g.name, t] = 1.0 switch_on[g.name, t] = (t == 1 ? 1.0 - _is_initially_on(g) : 0.0) @@ -34,7 +34,7 @@ function _add_status_eqs!( is_on = model[:is_on] switch_off = model[:switch_off] switch_on = model[:switch_on] - for t in 1:model[:instance].time + for t = 1:model[:instance].time if !g.must_run[t] # Link binary variables if t == 1 @@ -51,10 +51,8 @@ function _add_status_eqs!( ) end # Cannot switch on and off at the same time - eq_switch_on_off[g.name, t] = @constraint( - model, - switch_on[g.name, t] + switch_off[g.name, t] <= 1 - ) + eq_switch_on_off[g.name, t] = + @constraint(model, switch_on[g.name, t] + switch_off[g.name, t] <= 1) end end return diff --git a/src/model/formulations/KnuOstWat2018/pwlcosts.jl b/src/model/formulations/KnuOstWat2018/pwlcosts.jl index 85afa1e..42e76a4 100644 --- a/src/model/formulations/KnuOstWat2018/pwlcosts.jl +++ b/src/model/formulations/KnuOstWat2018/pwlcosts.jl @@ -26,12 +26,12 @@ function _add_production_piecewise_linear_eqs!( switch_on = model[:switch_on] switch_off = model[:switch_off] - for t in 1:T - for k in 1:K + for t = 1:T + for k = 1:K # Pbar^{k-1) Pbar0 = g.min_power[t] + - (k > 1 ? sum(g.cost_segments[ell].mw[t] for ell in 1:k-1) : 0.0) + (k > 1 ? sum(g.cost_segments[ell].mw[t] for ell = 1:k-1) : 0.0) # Pbar^k Pbar1 = g.cost_segments[k].mw[t] + Pbar0 @@ -61,8 +61,7 @@ function _add_production_piecewise_linear_eqs!( eq_segprod_limit_a[gn, t, k] = @constraint( model, segprod[gn, t, k] <= - g.cost_segments[k].mw[t] * is_on[gn, t] - - Cv * switch_on[gn, t] - + g.cost_segments[k].mw[t] * is_on[gn, t] - Cv * switch_on[gn, t] - (t < T ? Cw * switch_off[gn, t+1] : 0.0) ) else @@ -70,8 +69,7 @@ function _add_production_piecewise_linear_eqs!( eq_segprod_limit_b[gn, t, k] = @constraint( model, segprod[gn, t, k] <= - g.cost_segments[k].mw[t] * is_on[gn, t] - - Cv * switch_on[gn, t] - + g.cost_segments[k].mw[t] * is_on[gn, t] - Cv * switch_on[gn, t] - (t < T ? max(0, Cv - Cw) * switch_off[gn, t+1] : 0.0) ) @@ -87,18 +85,12 @@ function _add_production_piecewise_linear_eqs!( # Definition of production # Equation (43) in Kneuven et al. (2020) - eq_prod_above_def[gn, t] = @constraint( - model, - prod_above[gn, t] == sum(segprod[gn, t, k] for k in 1:K) - ) + eq_prod_above_def[gn, t] = + @constraint(model, prod_above[gn, t] == sum(segprod[gn, t, k] for k = 1:K)) # Objective function # Equation (44) in Kneuven et al. (2020) - add_to_expression!( - model[:obj], - segprod[gn, t, k], - g.cost_segments[k].cost[t], - ) + add_to_expression!(model[:obj], segprod[gn, t, k], g.cost_segments[k].cost[t]) # Also add an explicit upper bound on segprod to make the solver's # work a bit easier diff --git a/src/model/formulations/MorLatRam2013/ramp.jl b/src/model/formulations/MorLatRam2013/ramp.jl index cbb8f94..b8e07ec 100644 --- a/src/model/formulations/MorLatRam2013/ramp.jl +++ b/src/model/formulations/MorLatRam2013/ramp.jl @@ -32,9 +32,8 @@ function _add_ramp_eqs!( switch_off = model[:switch_off] switch_on = model[:switch_on] - for t in 1:model[:instance].time - time_invariant = - (t > 1) ? (abs(g.min_power[t] - g.min_power[t-1]) < 1e-7) : true + for t = 1:model[:instance].time + time_invariant = (t > 1) ? (abs(g.min_power[t] - g.min_power[t-1]) < 1e-7) : true # Ramp up limit if t == 1 @@ -60,8 +59,8 @@ function _add_ramp_eqs!( g.min_power[t] * is_on[gn, t] + prod_above[gn, t] + ( - RESERVES_WHEN_START_UP || RESERVES_WHEN_RAMP_UP ? - reserve[gn, t] : 0.0 + RESERVES_WHEN_START_UP || RESERVES_WHEN_RAMP_UP ? reserve[gn, t] : + 0.0 ) min_prod_last_period = g.min_power[t-1] * is_on[gn, t-1] + prod_above[gn, t-1] @@ -76,8 +75,7 @@ function _add_ramp_eqs!( # prod_above[gn, t] when starting up, and creates diff with (24). eq_ramp_up[gn, t] = @constraint( model, - prod_above[gn, t] + - (RESERVES_WHEN_RAMP_UP ? reserve[gn, t] : 0.0) - + prod_above[gn, t] + (RESERVES_WHEN_RAMP_UP ? reserve[gn, t] : 0.0) - prod_above[gn, t-1] <= RU ) end @@ -107,8 +105,7 @@ function _add_ramp_eqs!( RESERVES_WHEN_SHUT_DOWN || RESERVES_WHEN_RAMP_DOWN ? reserve[gn, t-1] : 0.0 ) - min_prod_this_period = - g.min_power[t] * is_on[gn, t] + prod_above[gn, t] + min_prod_this_period = g.min_power[t] * is_on[gn, t] + prod_above[gn, t] eq_ramp_down[gn, t] = @constraint( model, max_prod_last_period - min_prod_this_period <= diff --git a/src/model/formulations/MorLatRam2013/scosts.jl b/src/model/formulations/MorLatRam2013/scosts.jl index 2d68747..77ef984 100644 --- a/src/model/formulations/MorLatRam2013/scosts.jl +++ b/src/model/formulations/MorLatRam2013/scosts.jl @@ -11,30 +11,27 @@ function _add_startup_cost_eqs!( eq_startup_restrict = _init(model, :eq_startup_restrict) S = length(g.startup_categories) startup = model[:startup] - for t in 1:model[:instance].time + for t = 1:model[:instance].time # If unit is switching on, we must choose a startup category eq_startup_choose[g.name, t] = @constraint( model, - model[:switch_on][g.name, t] == - sum(startup[g.name, t, s] for s in 1:S) + model[:switch_on][g.name, t] == sum(startup[g.name, t, s] for s = 1:S) ) - for s in 1:S + for s = 1:S # If unit has not switched off in the last `delay` time periods, startup category is forbidden. # The last startup category is always allowed. if s < S range_start = t - g.startup_categories[s+1].delay + 1 range_end = t - g.startup_categories[s].delay range = (range_start:range_end) - initial_sum = ( - g.initial_status < 0 && (g.initial_status + 1 in range) ? 1.0 : 0.0 - ) + initial_sum = + (g.initial_status < 0 && (g.initial_status + 1 in range) ? 1.0 : 0.0) eq_startup_restrict[g.name, t, s] = @constraint( model, startup[g.name, t, s] <= - initial_sum + sum( - model[:switch_off][g.name, i] for i in range if i >= 1 - ) + initial_sum + + sum(model[:switch_off][g.name, i] for i in range if i >= 1) ) end diff --git a/src/model/formulations/PanGua2016/ramp.jl b/src/model/formulations/PanGua2016/ramp.jl index f040824..36415fe 100644 --- a/src/model/formulations/PanGua2016/ramp.jl +++ b/src/model/formulations/PanGua2016/ramp.jl @@ -14,10 +14,8 @@ function _add_ramp_eqs!( gn = g.name reserve = model[:reserve] eq_str_prod_limit = _init(model, :eq_str_prod_limit) - eq_prod_limit_ramp_up_extra_period = - _init(model, :eq_prod_limit_ramp_up_extra_period) - eq_prod_limit_shutdown_trajectory = - _init(model, :eq_prod_limit_shutdown_trajectory) + eq_prod_limit_ramp_up_extra_period = _init(model, :eq_prod_limit_ramp_up_extra_period) + eq_prod_limit_shutdown_trajectory = _init(model, :eq_prod_limit_shutdown_trajectory) UT = g.min_uptime SU = g.startup_limit # startup rate, i.e., max production right after startup SD = g.shutdown_limit # shutdown rate, i.e., max production right before shutdown @@ -33,7 +31,7 @@ function _add_ramp_eqs!( switch_off = model[:switch_off] switch_on = model[:switch_on] - for t in 1:T + for t = 1:T Pbar = g.max_power[t] if Pbar < 1e-7 # Skip this time period if max power = 0 @@ -54,13 +52,10 @@ function _add_ramp_eqs!( # then switch_off[gn, t+1] = 0 eq_str_prod_limit[gn, t] = @constraint( model, - prod_above[gn, t] + - g.min_power[t] * is_on[gn, t] + - reserve[gn, t] <= - Pbar * is_on[gn, t] - - (t < T ? (Pbar - SD) * switch_off[gn, t+1] : 0.0) - sum( + prod_above[gn, t] + g.min_power[t] * is_on[gn, t] + reserve[gn, t] <= + Pbar * is_on[gn, t] - (t < T ? (Pbar - SD) * switch_off[gn, t+1] : 0.0) - sum( (Pbar - (SU + i * RU)) * switch_on[gn, t-i] for - i in 0:min(UT - 2, TRU, t - 1) + i = 0:min(UT - 2, TRU, t - 1) ) ) @@ -69,12 +64,10 @@ function _add_ramp_eqs!( # Covers an additional time period of the ramp-up trajectory, compared to (38) eq_prod_limit_ramp_up_extra_period[gn, t] = @constraint( model, - prod_above[gn, t] + - g.min_power[t] * is_on[gn, t] + - reserve[gn, t] <= + prod_above[gn, t] + g.min_power[t] * is_on[gn, t] + reserve[gn, t] <= Pbar * is_on[gn, t] - sum( (Pbar - (SU + i * RU)) * switch_on[gn, t-i] for - i in 0:min(UT - 1, TRU, t - 1) + i = 0:min(UT - 1, TRU, t - 1) ) ) end @@ -89,13 +82,9 @@ function _add_ramp_eqs!( prod_above[gn, t] + g.min_power[t] * is_on[gn, t] + (RESERVES_WHEN_SHUT_DOWN ? reserve[gn, t] : 0.0) <= - Pbar * is_on[gn, t] - sum( - (Pbar - (SD + i * RD)) * switch_off[gn, t+1+i] for - i in 0:KSD - ) - sum( - (Pbar - (SU + i * RU)) * switch_on[gn, t-i] for - i in 0:KSU - ) - ( + Pbar * is_on[gn, t] - + sum((Pbar - (SD + i * RD)) * switch_off[gn, t+1+i] for i = 0:KSD) - + sum((Pbar - (SU + i * RU)) * switch_on[gn, t-i] for i = 0:KSU) - ( (KSU >= TRU || KSU > t - 2) ? 0.0 : max(0, (SU + (KSU + 1) * RU) - (SD + TRD * RD)) * switch_on[gn, t-(KSU+1)] diff --git a/src/model/formulations/WanHob2016/ramp.jl b/src/model/formulations/WanHob2016/ramp.jl index 8a999f1..0580875 100644 --- a/src/model/formulations/WanHob2016/ramp.jl +++ b/src/model/formulations/WanHob2016/ramp.jl @@ -8,7 +8,7 @@ function _add_flexiramp_vars!(model::JuMP.Model, g::Unit)::Nothing mfg = _init(model, :mfg) dwflexiramp = _init(model, :dwflexiramp) dwflexiramp_shortfall = _init(model, :dwflexiramp_shortfall) - for t in 1:model[:instance].time + for t = 1:model[:instance].time # maximum feasible generation, \bar{g_{its}} in Wang & Hobbs (2016) mfg[g.name, t] = @variable(model, lower_bound = 0) if g.provides_flexiramp_reserves[t] @@ -51,37 +51,28 @@ function _add_ramp_eqs!( dwflexiramp = model[:dwflexiramp] mfg = model[:mfg] - for t in 1:model[:instance].time - @constraint( - model, - prod_above[gn, t] + (is_on[gn, t] * minp[t]) <= mfg[gn, t] - ) # Eq. (19) in Wang & Hobbs (2016) + for t = 1:model[:instance].time + @constraint(model, prod_above[gn, t] + (is_on[gn, t] * minp[t]) <= mfg[gn, t]) # Eq. (19) in Wang & Hobbs (2016) @constraint(model, mfg[gn, t] <= is_on[gn, t] * maxp[t]) # Eq. (22) in Wang & Hobbs (2016) if t != model[:instance].time @constraint( model, minp[t] * (is_on[gn, t+1] + is_on[gn, t] - 1) <= - prod_above[gn, t] - dwflexiramp[gn, t] + - (is_on[gn, t] * minp[t]) + prod_above[gn, t] - dwflexiramp[gn, t] + (is_on[gn, t] * minp[t]) ) # first inequality of Eq. (20) in Wang & Hobbs (2016) @constraint( model, - prod_above[gn, t] - dwflexiramp[gn, t] + - (is_on[gn, t] * minp[t]) <= + prod_above[gn, t] - dwflexiramp[gn, t] + (is_on[gn, t] * minp[t]) <= mfg[gn, t+1] + (maxp[t] * (1 - is_on[gn, t+1])) ) # second inequality of Eq. (20) in Wang & Hobbs (2016) @constraint( model, minp[t] * (is_on[gn, t+1] + is_on[gn, t] - 1) <= - prod_above[gn, t] + - upflexiramp[gn, t] + - (is_on[gn, t] * minp[t]) + prod_above[gn, t] + upflexiramp[gn, t] + (is_on[gn, t] * minp[t]) ) # first inequality of Eq. (21) in Wang & Hobbs (2016) @constraint( model, - prod_above[gn, t] + - upflexiramp[gn, t] + - (is_on[gn, t] * minp[t]) <= + prod_above[gn, t] + upflexiramp[gn, t] + (is_on[gn, t] * minp[t]) <= mfg[gn, t+1] + (maxp[t] * (1 - is_on[gn, t+1])) ) # second inequality of Eq. (21) in Wang & Hobbs (2016) if t != 1 @@ -113,8 +104,7 @@ function _add_ramp_eqs!( ) # Eq. (23) in Wang & Hobbs (2016) for the first time period @constraint( model, - initial_power - - (prod_above[gn, t] + (is_on[gn, t] * minp[t])) <= + initial_power - (prod_above[gn, t] + (is_on[gn, t] * minp[t])) <= RD * is_on[gn, t] + SD * (is_initially_on - is_on[gn, t]) + maxp[t] * (1 - is_initially_on) @@ -123,8 +113,7 @@ function _add_ramp_eqs!( @constraint( model, mfg[gn, t] <= - (SD * (is_on[gn, t] - is_on[gn, t+1])) + - (maxp[t] * is_on[gn, t+1]) + (SD * (is_on[gn, t] - is_on[gn, t+1])) + (maxp[t] * is_on[gn, t+1]) ) # Eq. (24) in Wang & Hobbs (2016) @constraint( model, @@ -152,15 +141,13 @@ function _add_ramp_eqs!( ) # second inequality of Eq. (27) in Wang & Hobbs (2016) @constraint( model, - -maxp[t] * is_on[gn, t] + minp[t] * is_on[gn, t+1] <= - upflexiramp[gn, t] + -maxp[t] * is_on[gn, t] + minp[t] * is_on[gn, t+1] <= upflexiramp[gn, t] ) # first inequality of Eq. (28) in Wang & Hobbs (2016) @constraint(model, upflexiramp[gn, t] <= maxp[t] * is_on[gn, t+1]) # second inequality of Eq. (28) in Wang & Hobbs (2016) @constraint(model, -maxp[t] * is_on[gn, t+1] <= dwflexiramp[gn, t]) # first inequality of Eq. (29) in Wang & Hobbs (2016) @constraint( model, - dwflexiramp[gn, t] <= - (maxp[t] * is_on[gn, t]) - (minp[t] * is_on[gn, t+1]) + dwflexiramp[gn, t] <= (maxp[t] * is_on[gn, t]) - (minp[t] * is_on[gn, t+1]) ) # second inequality of Eq. (29) in Wang & Hobbs (2016) else @constraint( diff --git a/src/model/formulations/base/bus.jl b/src/model/formulations/base/bus.jl index d881fc6..f938c9c 100644 --- a/src/model/formulations/base/bus.jl +++ b/src/model/formulations/base/bus.jl @@ -5,13 +5,12 @@ function _add_bus!(model::JuMP.Model, b::Bus)::Nothing net_injection = _init(model, :expr_net_injection) curtail = _init(model, :curtail) - for t in 1:model[:instance].time + for t = 1:model[:instance].time # Fixed load net_injection[b.name, t] = AffExpr(-b.load[t]) # Load curtailment - curtail[b.name, t] = - @variable(model, lower_bound = 0, upper_bound = b.load[t]) + curtail[b.name, t] = @variable(model, lower_bound = 0, upper_bound = b.load[t]) add_to_expression!(net_injection[b.name, t], curtail[b.name, t], 1.0) add_to_expression!( diff --git a/src/model/formulations/base/line.jl b/src/model/formulations/base/line.jl index 6398c8d..fe6d24b 100644 --- a/src/model/formulations/base/line.jl +++ b/src/model/formulations/base/line.jl @@ -8,13 +8,9 @@ function _add_transmission_line!( f::ShiftFactorsFormulation, )::Nothing overflow = _init(model, :overflow) - for t in 1:model[:instance].time + for t = 1:model[:instance].time overflow[lm.name, t] = @variable(model, lower_bound = 0) - add_to_expression!( - model[:obj], - overflow[lm.name, t], - lm.flow_limit_penalty[t], - ) + add_to_expression!(model[:obj], overflow[lm.name, t], lm.flow_limit_penalty[t]) end return end diff --git a/src/model/formulations/base/psload.jl b/src/model/formulations/base/psload.jl index 3748111..de9355e 100644 --- a/src/model/formulations/base/psload.jl +++ b/src/model/formulations/base/psload.jl @@ -2,26 +2,18 @@ # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Released under the modified BSD license. See COPYING.md for more details. -function _add_price_sensitive_load!( - model::JuMP.Model, - ps::PriceSensitiveLoad, -)::Nothing +function _add_price_sensitive_load!(model::JuMP.Model, ps::PriceSensitiveLoad)::Nothing loads = _init(model, :loads) net_injection = _init(model, :expr_net_injection) - for t in 1:model[:instance].time + for t = 1:model[:instance].time # Decision variable - loads[ps.name, t] = - @variable(model, lower_bound = 0, upper_bound = ps.demand[t]) + loads[ps.name, t] = @variable(model, lower_bound = 0, upper_bound = ps.demand[t]) # Objective function terms add_to_expression!(model[:obj], loads[ps.name, t], -ps.revenue[t]) # Net injection - add_to_expression!( - net_injection[ps.bus.name, t], - loads[ps.name, t], - -1.0, - ) + add_to_expression!(net_injection[ps.bus.name, t], loads[ps.name, t], -1.0) end return end diff --git a/src/model/formulations/base/sensitivity.jl b/src/model/formulations/base/sensitivity.jl index f2fca9a..6bf153f 100644 --- a/src/model/formulations/base/sensitivity.jl +++ b/src/model/formulations/base/sensitivity.jl @@ -13,10 +13,7 @@ M[l.offset, b.offset] indicates the amount of power (in MW) that flows through transmission line l when 1 MW of power is injected at the slack bus (the bus that has offset zero) and withdrawn from b. """ -function _injection_shift_factors(; - buses::Array{Bus}, - lines::Array{TransmissionLine}, -) +function _injection_shift_factors(; buses::Array{Bus}, lines::Array{TransmissionLine}) susceptance = _susceptance_matrix(lines) incidence = _reduced_incidence_matrix(lines = lines, buses = buses) laplacian = transpose(incidence) * susceptance * incidence @@ -33,10 +30,7 @@ is the number of buses and L is the number of lines. For each row, there is a 1 element and a -1 element, indicating the source and target buses, respectively, for that line. """ -function _reduced_incidence_matrix(; - buses::Array{Bus}, - lines::Array{TransmissionLine}, -) +function _reduced_incidence_matrix(; buses::Array{Bus}, lines::Array{TransmissionLine}) matrix = spzeros(Float64, length(lines), length(buses) - 1) for line in lines if line.source.offset > 0 @@ -75,7 +69,7 @@ function _line_outage_factors(; incidence = Array(_reduced_incidence_matrix(lines = lines, buses = buses)) lodf::Array{Float64,2} = isf * transpose(incidence) _, n = size(lodf) - for i in 1:n + for i = 1:n lodf[:, i] *= 1.0 / (1.0 - lodf[i, i]) lodf[i, i] = -1 end diff --git a/src/model/formulations/base/structs.jl b/src/model/formulations/base/structs.jl index 2cc7e44..37358de 100644 --- a/src/model/formulations/base/structs.jl +++ b/src/model/formulations/base/structs.jl @@ -25,14 +25,7 @@ struct Formulation status_vars::StatusVarsFormulation = Gar1962.StatusVars(), transmission::TransmissionFormulation = ShiftFactorsFormulation(), ) - return new( - prod_vars, - pwl_costs, - ramping, - startup_costs, - status_vars, - transmission, - ) + return new(prod_vars, pwl_costs, ramping, startup_costs, status_vars, transmission) end end diff --git a/src/model/formulations/base/system.jl b/src/model/formulations/base/system.jl index c526040..8de941f 100644 --- a/src/model/formulations/base/system.jl +++ b/src/model/formulations/base/system.jl @@ -14,12 +14,12 @@ function _add_net_injection_eqs!(model::JuMP.Model)::Nothing net_injection = _init(model, :net_injection) eq_net_injection = _init(model, :eq_net_injection) eq_power_balance = _init(model, :eq_power_balance) - for t in 1:T, b in model[:instance].buses + for t = 1:T, b in model[:instance].buses n = net_injection[b.name, t] = @variable(model) eq_net_injection[b.name, t] = @constraint(model, -n + model[:expr_net_injection][b.name, t] == 0) end - for t in 1:T + for t = 1:T eq_power_balance[t] = @constraint( model, sum(net_injection[b.name, t] for b in model[:instance].buses) == 0 @@ -31,7 +31,7 @@ end function _add_reserve_eqs!(model::JuMP.Model)::Nothing eq_min_reserve = _init(model, :eq_min_reserve) instance = model[:instance] - for t in 1:instance.time + for t = 1:instance.time # Equation (68) in Kneuven et al. (2020) # As in Morales-España et al. (2013a) # Akin to the alternative formulation with max_power_avail @@ -46,11 +46,7 @@ function _add_reserve_eqs!(model::JuMP.Model)::Nothing # Account for shortfall contribution to objective if shortfall_penalty >= 0 - add_to_expression!( - model[:obj], - shortfall_penalty, - model[:reserve_shortfall][t], - ) + add_to_expression!(model[:obj], shortfall_penalty, model[:reserve_shortfall][t]) end end return @@ -65,24 +61,20 @@ function _add_flexiramp_eqs!(model::JuMP.Model)::Nothing eq_min_upflexiramp = _init(model, :eq_min_upflexiramp) eq_min_dwflexiramp = _init(model, :eq_min_dwflexiramp) instance = model[:instance] - for t in 1:instance.time + for t = 1:instance.time flexiramp_shortfall_penalty = instance.flexiramp_shortfall_penalty[t] # Eq. (17) in Wang & Hobbs (2016) eq_min_upflexiramp[t] = @constraint( model, - sum(model[:upflexiramp][g.name, t] for g in instance.units) + - ( - flexiramp_shortfall_penalty >= 0 ? - model[:upflexiramp_shortfall][t] : 0.0 + sum(model[:upflexiramp][g.name, t] for g in instance.units) + ( + flexiramp_shortfall_penalty >= 0 ? model[:upflexiramp_shortfall][t] : 0.0 ) >= instance.reserves.upflexiramp[t] ) # Eq. (18) in Wang & Hobbs (2016) eq_min_dwflexiramp[t] = @constraint( model, - sum(model[:dwflexiramp][g.name, t] for g in instance.units) + - ( - flexiramp_shortfall_penalty >= 0 ? - model[:dwflexiramp_shortfall][t] : 0.0 + sum(model[:dwflexiramp][g.name, t] for g in instance.units) + ( + flexiramp_shortfall_penalty >= 0 ? model[:dwflexiramp_shortfall][t] : 0.0 ) >= instance.reserves.dwflexiramp[t] ) @@ -91,10 +83,7 @@ function _add_flexiramp_eqs!(model::JuMP.Model)::Nothing add_to_expression!( model[:obj], flexiramp_shortfall_penalty, - ( - model[:upflexiramp_shortfall][t] + - model[:dwflexiramp_shortfall][t] - ), + (model[:upflexiramp_shortfall][t] + model[:dwflexiramp_shortfall][t]), ) end end diff --git a/src/model/formulations/base/unit.jl b/src/model/formulations/base/unit.jl index 5da9ffa..e3bbe45 100644 --- a/src/model/formulations/base/unit.jl +++ b/src/model/formulations/base/unit.jl @@ -46,7 +46,7 @@ _is_initially_on(g::Unit)::Float64 = (g.initial_status > 0 ? 1.0 : 0.0) function _add_reserve_vars!(model::JuMP.Model, g::Unit)::Nothing reserve = _init(model, :reserve) reserve_shortfall = _init(model, :reserve_shortfall) - for t in 1:model[:instance].time + for t = 1:model[:instance].time if g.provides_spinning_reserves[t] reserve[g.name, t] = @variable(model, lower_bound = 0) else @@ -61,7 +61,7 @@ end function _add_reserve_eqs!(model::JuMP.Model, g::Unit)::Nothing reserve = model[:reserve] - for t in 1:model[:instance].time + for t = 1:model[:instance].time add_to_expression!(expr_reserve[g.bus.name, t], reserve[g.name, t], 1.0) end return @@ -69,8 +69,8 @@ end function _add_startup_shutdown_vars!(model::JuMP.Model, g::Unit)::Nothing startup = _init(model, :startup) - for t in 1:model[:instance].time - for s in 1:length(g.startup_categories) + for t = 1:model[:instance].time + for s = 1:length(g.startup_categories) startup[g.name, t, s] = @variable(model, binary = true) end end @@ -86,7 +86,7 @@ function _add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing switch_off = model[:switch_off] switch_on = model[:switch_on] T = model[:instance].time - for t in 1:T + for t = 1:T # Startup limit eq_startup_limit[g.name, t] = @constraint( model, @@ -96,16 +96,14 @@ function _add_startup_shutdown_limit_eqs!(model::JuMP.Model, g::Unit)::Nothing ) # Shutdown limit if g.initial_power > g.shutdown_limit - eq_shutdown_limit[g.name, 0] = - @constraint(model, switch_off[g.name, 1] <= 0) + eq_shutdown_limit[g.name, 0] = @constraint(model, switch_off[g.name, 1] <= 0) end if t < T eq_shutdown_limit[g.name, t] = @constraint( model, prod_above[g.name, t] <= (g.max_power[t] - g.min_power[t]) * is_on[g.name, t] - - max(0, g.max_power[t] - g.shutdown_limit) * - switch_off[g.name, t+1] + max(0, g.max_power[t] - g.shutdown_limit) * switch_off[g.name, t+1] ) end end @@ -121,7 +119,7 @@ function _add_ramp_eqs!( reserve = model[:reserve] eq_ramp_up = _init(model, :eq_ramp_up) eq_ramp_down = _init(model, :eq_ramp_down) - for t in 1:model[:instance].time + for t = 1:model[:instance].time # Ramp up limit if t == 1 if _is_initially_on(g) == 1 @@ -151,8 +149,7 @@ function _add_ramp_eqs!( else eq_ramp_down[g.name, t] = @constraint( model, - prod_above[g.name, t] >= - prod_above[g.name, t-1] - g.ramp_down_limit + prod_above[g.name, t] >= prod_above[g.name, t-1] - g.ramp_down_limit ) end end @@ -165,18 +162,18 @@ function _add_min_uptime_downtime_eqs!(model::JuMP.Model, g::Unit)::Nothing eq_min_uptime = _init(model, :eq_min_uptime) eq_min_downtime = _init(model, :eq_min_downtime) T = model[:instance].time - for t in 1:T + for t = 1:T # Minimum up-time eq_min_uptime[g.name, t] = @constraint( model, - sum(switch_on[g.name, i] for i in (t-g.min_uptime+1):t if i >= 1) <= is_on[g.name, t] + sum(switch_on[g.name, i] for i in (t-g.min_uptime+1):t if i >= 1) <= + is_on[g.name, t] ) # Minimum down-time eq_min_downtime[g.name, t] = @constraint( model, - sum( - switch_off[g.name, i] for i in (t-g.min_downtime+1):t if i >= 1 - ) <= 1 - is_on[g.name, t] + sum(switch_off[g.name, i] for i in (t-g.min_downtime+1):t if i >= 1) <= + 1 - is_on[g.name, t] ) # Minimum up/down-time for initial periods if t == 1 @@ -203,7 +200,7 @@ end function _add_net_injection_eqs!(model::JuMP.Model, g::Unit)::Nothing expr_net_injection = model[:expr_net_injection] - for t in 1:model[:instance].time + for t = 1:model[:instance].time # Add to net injection expression add_to_expression!( expr_net_injection[g.bus.name, t], diff --git a/src/solution/fix.jl b/src/solution/fix.jl index f9deacb..dbead66 100644 --- a/src/solution/fix.jl +++ b/src/solution/fix.jl @@ -14,12 +14,10 @@ function fix!(model::JuMP.Model, solution::AbstractDict)::Nothing prod_above = model[:prod_above] reserve = model[:reserve] for g in instance.units - for t in 1:T + for t = 1:T is_on_value = round(solution["Is on"][g.name][t]) - prod_value = - round(solution["Production (MW)"][g.name][t], digits = 5) - reserve_value = - round(solution["Reserve (MW)"][g.name][t], digits = 5) + prod_value = round(solution["Production (MW)"][g.name][t], digits = 5) + reserve_value = round(solution["Reserve (MW)"][g.name][t], digits = 5) JuMP.fix(is_on[g.name, t], is_on_value, force = true) JuMP.fix( prod_above[g.name, t], diff --git a/src/solution/methods/XavQiuWanThi2019/enforce.jl b/src/solution/methods/XavQiuWanThi2019/enforce.jl index 526274f..08ae1af 100644 --- a/src/solution/methods/XavQiuWanThi2019/enforce.jl +++ b/src/solution/methods/XavQiuWanThi2019/enforce.jl @@ -2,10 +2,7 @@ # Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved. # Released under the modified BSD license. See COPYING.md for more details. -function _enforce_transmission( - model::JuMP.Model, - violations::Vector{_Violation}, -)::Nothing +function _enforce_transmission(model::JuMP.Model, violations::Vector{_Violation})::Nothing for v in violations _enforce_transmission( model = model, diff --git a/src/solution/methods/XavQiuWanThi2019/filter.jl b/src/solution/methods/XavQiuWanThi2019/filter.jl index 576e8fc..a70ef78 100644 --- a/src/solution/methods/XavQiuWanThi2019/filter.jl +++ b/src/solution/methods/XavQiuWanThi2019/filter.jl @@ -4,11 +4,9 @@ function _offer(filter::_ViolationFilter, v::_Violation)::Nothing if v.monitored_line.offset ∉ keys(filter.queues) - filter.queues[v.monitored_line.offset] = - PriorityQueue{_Violation,Float64}() + filter.queues[v.monitored_line.offset] = PriorityQueue{_Violation,Float64}() end - q::PriorityQueue{_Violation,Float64} = - filter.queues[v.monitored_line.offset] + q::PriorityQueue{_Violation,Float64} = filter.queues[v.monitored_line.offset] if length(q) < filter.max_per_line enqueue!(q, v => v.amount) else diff --git a/src/solution/methods/XavQiuWanThi2019/find.jl b/src/solution/methods/XavQiuWanThi2019/find.jl index 3cf9094..11f8bd9 100644 --- a/src/solution/methods/XavQiuWanThi2019/find.jl +++ b/src/solution/methods/XavQiuWanThi2019/find.jl @@ -4,11 +4,7 @@ import Base.Threads: @threads -function _find_violations( - model::JuMP.Model; - max_per_line::Int, - max_per_period::Int, -) +function _find_violations(model::JuMP.Model; max_per_line::Int, max_per_period::Int) instance = model[:instance] net_injection = model[:net_injection] overflow = model[:overflow] @@ -18,13 +14,10 @@ function _find_violations( time_screening = @elapsed begin non_slack_buses = [b for b in instance.buses if b.offset > 0] net_injection_values = [ - value(net_injection[b.name, t]) for b in non_slack_buses, - t in 1:instance.time - ] - overflow_values = [ - value(overflow[lm.name, t]) for lm in instance.lines, - t in 1:instance.time + value(net_injection[b.name, t]) for b in non_slack_buses, t = 1:instance.time ] + overflow_values = + [value(overflow[lm.name, t]) for lm in instance.lines, t = 1:instance.time] violations = UnitCommitment._find_violations( instance = instance, net_injections = net_injection_values, @@ -35,10 +28,7 @@ function _find_violations( max_per_period = max_per_period, ) end - @info @sprintf( - "Verified transmission limits in %.2f seconds", - time_screening - ) + @info @sprintf("Verified transmission limits in %.2f seconds", time_screening) return violations end @@ -81,10 +71,8 @@ function _find_violations(; size(lodf) == (L, L) || error("lodf has incorrect size") filters = Dict( - t => _ViolationFilter( - max_total = max_per_period, - max_per_line = max_per_line, - ) for t in 1:T + t => _ViolationFilter(max_total = max_per_period, max_per_line = max_per_line) + for t = 1:T ) pre_flow::Array{Float64} = zeros(L, K) # pre_flow[lm, thread] @@ -92,35 +80,30 @@ function _find_violations(; pre_v::Array{Float64} = zeros(L, K) # pre_v[lm, thread] post_v::Array{Float64} = zeros(L, L, K) # post_v[lm, lc, thread] - normal_limits::Array{Float64,2} = [ - l.normal_flow_limit[t] + overflow[l.offset, t] for - l in instance.lines, t in 1:T - ] + normal_limits::Array{Float64,2} = + [l.normal_flow_limit[t] + overflow[l.offset, t] for l in instance.lines, t = 1:T] - emergency_limits::Array{Float64,2} = [ - l.emergency_flow_limit[t] + overflow[l.offset, t] for - l in instance.lines, t in 1:T - ] + emergency_limits::Array{Float64,2} = + [l.emergency_flow_limit[t] + overflow[l.offset, t] for l in instance.lines, t = 1:T] is_vulnerable::Array{Bool} = zeros(Bool, L) for c in instance.contingencies is_vulnerable[c.lines[1].offset] = true end - @threads for t in 1:T + @threads for t = 1:T k = threadid() # Pre-contingency flows pre_flow[:, k] = isf * net_injections[:, t] # Post-contingency flows - for lc in 1:L, lm in 1:L - post_flow[lm, lc, k] = - pre_flow[lm, k] + pre_flow[lc, k] * lodf[lm, lc] + for lc = 1:L, lm = 1:L + post_flow[lm, lc, k] = pre_flow[lm, k] + pre_flow[lc, k] * lodf[lm, lc] end # Pre-contingency violations - for lm in 1:L + for lm = 1:L pre_v[lm, k] = max( 0.0, pre_flow[lm, k] - normal_limits[lm, t], @@ -129,7 +112,7 @@ function _find_violations(; end # Post-contingency violations - for lc in 1:L, lm in 1:L + for lc = 1:L, lm = 1:L post_v[lm, lc, k] = max( 0.0, post_flow[lm, lc, k] - emergency_limits[lm, t], @@ -138,7 +121,7 @@ function _find_violations(; end # Offer pre-contingency violations - for lm in 1:L + for lm = 1:L if pre_v[lm, k] > 1e-5 _offer( filters[t], @@ -153,7 +136,7 @@ function _find_violations(; end # Offer post-contingency violations - for lm in 1:L, lc in 1:L + for lm = 1:L, lc = 1:L if post_v[lm, lc, k] > 1e-5 && is_vulnerable[lc] _offer( filters[t], @@ -169,7 +152,7 @@ function _find_violations(; end violations = _Violation[] - for t in 1:instance.time + for t = 1:instance.time append!(violations, _query(filters[t])) end diff --git a/src/solution/methods/XavQiuWanThi2019/optimize.jl b/src/solution/methods/XavQiuWanThi2019/optimize.jl index b513bd1..9362da6 100644 --- a/src/solution/methods/XavQiuWanThi2019/optimize.jl +++ b/src/solution/methods/XavQiuWanThi2019/optimize.jl @@ -27,10 +27,7 @@ function optimize!(model::JuMP.Model, method::XavQiuWanThi2019.Method)::Nothing @info "Time limit exceeded" break end - @info @sprintf( - "Setting MILP time limit to %.2f seconds", - time_remaining - ) + @info @sprintf("Setting MILP time limit to %.2f seconds", time_remaining) JuMP.set_time_limit_sec(model, time_remaining) @info "Solving MILP..." JuMP.optimize!(model) diff --git a/src/solution/solution.jl b/src/solution/solution.jl index 0283eac..0fdb772 100644 --- a/src/solution/solution.jl +++ b/src/solution/solution.jl @@ -6,43 +6,40 @@ 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 + b.name => [round(value(vars[b.name, t]), digits = 5) for t = 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) + value(model[:segprod][g.name, t, k]) * g.cost_segments[k].cost[t] for + k = 1:length(g.cost_segments) ], - ) for t in 1:T + ) for t = 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) + value(model[:segprod][g.name, t, k]) for k = 1:length(g.cost_segments) ], - ) for t in 1:T + ) for t = 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 + g.startup_categories[s].cost * value(model[:startup][g.name, t, s]) for + s = 1:S + ) for t = 1:T ] end sol = OrderedDict() - sol["Production (MW)"] = - OrderedDict(g.name => production(g) for g in instance.units) + 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 (\$)"] = @@ -54,21 +51,19 @@ function solution(model::JuMP.Model)::OrderedDict 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 (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 + round(value(model[:upflexiramp_shortfall][t]), digits = 5) : 0.0 for + t = 1:instance.time ) - sol["Down-flexiramp (MW)"] = - timeseries(model[:dwflexiramp], instance.units) + 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 + round(value(model[:dwflexiramp_shortfall][t]), digits = 5) : 0.0 for + t = 1:instance.time ) else # Report spinning reserve solutions only if both up-flexiramp and @@ -77,12 +72,11 @@ function solution(model::JuMP.Model)::OrderedDict 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 + round(value(model[:reserve_shortfall][t]), digits = 5) : 0.0 for + t = 1:instance.time ) end - sol["Net injection (MW)"] = - timeseries(model[:net_injection], instance.buses) + 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) diff --git a/src/solution/warmstart.jl b/src/solution/warmstart.jl index 678f250..38c9135 100644 --- a/src/solution/warmstart.jl +++ b/src/solution/warmstart.jl @@ -6,16 +6,10 @@ function set_warm_start!(model::JuMP.Model, solution::AbstractDict)::Nothing instance, T = model[:instance], model[:instance].time is_on = model[:is_on] for g in instance.units - for t in 1:T + for t = 1:T JuMP.set_start_value(is_on[g.name, t], solution["Is on"][g.name][t]) - JuMP.set_start_value( - switch_on[g.name, t], - solution["Switch on"][g.name][t], - ) - JuMP.set_start_value( - switch_off[g.name, t], - solution["Switch off"][g.name][t], - ) + JuMP.set_start_value(switch_on[g.name, t], solution["Switch on"][g.name][t]) + JuMP.set_start_value(switch_off[g.name, t], solution["Switch off"][g.name][t]) end end return diff --git a/src/transform/initcond.jl b/src/transform/initcond.jl index 6bbf0ea..9ca733e 100644 --- a/src/transform/initcond.jl +++ b/src/transform/initcond.jl @@ -11,10 +11,7 @@ Generates feasible initial conditions for the given instance, by constructing and solving a single-period mixed-integer optimization problem, using the given optimizer. The instance is modified in-place. """ -function generate_initial_conditions!( - instance::UnitCommitmentInstance, - optimizer, -)::Nothing +function generate_initial_conditions!(instance::UnitCommitmentInstance, optimizer)::Nothing G = instance.units B = instance.buses t = 1 @@ -31,11 +28,7 @@ function generate_initial_conditions!( @constraint(mip, max_power[g in G], p[g] <= g.max_power[t] * x[g]) # Constraint: Production equals demand - @constraint( - mip, - power_balance, - sum(b.load[t] for b in B) == sum(p[g] for g in G) - ) + @constraint(mip, power_balance, sum(b.load[t] for b in B) == sum(p[g] for g in G)) # Constraint: Must run for g in G diff --git a/src/transform/randomize/XavQiuAhm2021.jl b/src/transform/randomize/XavQiuAhm2021.jl index adedcd2..73bc5d5 100644 --- a/src/transform/randomize/XavQiuAhm2021.jl +++ b/src/transform/randomize/XavQiuAhm2021.jl @@ -117,10 +117,7 @@ Base.@kwdef struct Randomization randomize_load_share::Bool = true end -function _randomize_costs( - instance::UnitCommitmentInstance, - distribution, -)::Nothing +function _randomize_costs(instance::UnitCommitmentInstance, distribution)::Nothing for unit in instance.units α = rand(distribution) unit.min_power_cost *= α @@ -134,17 +131,11 @@ function _randomize_costs( return end -function _randomize_load_share( - instance::UnitCommitmentInstance, - distribution, -)::Nothing +function _randomize_load_share(instance::UnitCommitmentInstance, distribution)::Nothing α = rand(distribution, length(instance.buses)) - for t in 1:instance.time + for t = 1:instance.time total = sum(bus.load[t] for bus in instance.buses) - den = sum( - bus.load[t] / total * α[i] for - (i, bus) in enumerate(instance.buses) - ) + den = sum(bus.load[t] / total * α[i] for (i, bus) in enumerate(instance.buses)) for (i, bus) in enumerate(instance.buses) bus.load[t] *= α[i] / den end @@ -158,11 +149,9 @@ function _randomize_load_profile( )::Nothing # Generate new system load system_load = [1.0] - for t in 2:instance.time + for t = 2:instance.time idx = (t - 1) % length(params.load_profile_mu) + 1 - gamma = rand( - Normal(params.load_profile_mu[idx], params.load_profile_sigma[idx]), - ) + gamma = rand(Normal(params.load_profile_mu[idx], params.load_profile_sigma[idx])) push!(system_load, system_load[t-1] * gamma) end capacity = sum(maximum(u.max_power) for u in instance.units) @@ -172,7 +161,7 @@ function _randomize_load_profile( # Scale bus loads to match the new system load prev_system_load = sum(b.load for b in instance.buses) for b in instance.buses - for t in 1:instance.time + for t = 1:instance.time b.load[t] *= system_load[t] / prev_system_load[t] end end diff --git a/src/utils/benchmark.jl b/src/utils/benchmark.jl index ba26f1d..159febb 100644 --- a/src/utils/benchmark.jl +++ b/src/utils/benchmark.jl @@ -93,9 +93,8 @@ function _run_benchmarks(; trials, ) combinations = [ - (c, s.first, s.second, m.first, m.second, f.first, f.second, t) for - c in cases for s in optimizers for f in formulations for - m in methods for t in trials + (c, s.first, s.second, m.first, m.second, f.first, f.second, t) for c in cases + for s in optimizers for f in formulations for m in methods for t in trials ] shuffle!(combinations) if nworkers() > 1 diff --git a/src/validation/repair.jl b/src/validation/repair.jl index e1dd964..5d37523 100644 --- a/src/validation/repair.jl +++ b/src/validation/repair.jl @@ -18,7 +18,7 @@ function repair!(instance::UnitCommitmentInstance)::Int for g in instance.units # Startup costs and delays must be increasing - for s in 2:length(g.startup_categories) + for s = 2:length(g.startup_categories) if g.startup_categories[s].delay <= g.startup_categories[s-1].delay prev_value = g.startup_categories[s].delay new_value = g.startup_categories[s-1].delay + 1 @@ -38,9 +38,9 @@ function repair!(instance::UnitCommitmentInstance)::Int end end - for t in 1:instance.time + for t = 1:instance.time # Production cost curve should be convex - for k in 2:length(g.cost_segments) + for k = 2:length(g.cost_segments) cost = g.cost_segments[k].cost[t] min_cost = g.cost_segments[k-1].cost[t] if cost < min_cost - 1e-5 diff --git a/src/validation/validate.jl b/src/validation/validate.jl index 72da3c7..395bd48 100644 --- a/src/validation/validate.jl +++ b/src/validation/validate.jl @@ -24,10 +24,7 @@ This function is implemented independently from the optimization model in producing valid solutions. It can also be used to verify the solutions produced by other optimization packages. """ -function validate( - instance::UnitCommitmentInstance, - solution::Union{Dict,OrderedDict}, -)::Bool +function validate(instance::UnitCommitmentInstance, solution::Union{Dict,OrderedDict})::Bool err_count = 0 err_count += _validate_units(instance, solution) err_count += _validate_reserve_and_demand(instance, solution) @@ -50,13 +47,12 @@ function _validate_units(instance, solution; tol = 0.01) actual_startup_cost = solution["Startup cost (\$)"][unit.name] is_on = bin(solution["Is on"][unit.name]) - for t in 1:instance.time + for t = 1:instance.time # Auxiliary variables if t == 1 is_starting_up = (unit.initial_status < 0) && is_on[t] is_shutting_down = (unit.initial_status > 0) && !is_on[t] - ramp_up = - max(0, production[t] + reserve[t] - unit.initial_power) + ramp_up = max(0, production[t] + reserve[t] - unit.initial_power) ramp_down = max(0, unit.initial_power - production[t]) else is_starting_up = !is_on[t-1] && is_on[t] @@ -90,11 +86,7 @@ function _validate_units(instance, solution; tol = 0.01) # Verify must-run if !is_on[t] && unit.must_run[t] - @error @sprintf( - "Must-run unit %s is offline at time %d", - unit.name, - t - ) + @error @sprintf("Must-run unit %s is offline at time %d", unit.name, t) err_count += 1 end @@ -121,8 +113,7 @@ function _validate_units(instance, solution; tol = 0.01) end # If unit is on, must produce at most its maximum power - if is_on[t] && - (production[t] + reserve[t] > unit.max_power[t] + tol) + if is_on[t] && (production[t] + reserve[t] > unit.max_power[t] + tol) @error @sprintf( "Unit %s produces above its maximum limit at time %d (%.2f + %.2f> %.2f)", unit.name, @@ -136,11 +127,7 @@ function _validate_units(instance, solution; tol = 0.01) # If unit is off, must produce zero if !is_on[t] && production[t] + reserve[t] > tol - @error @sprintf( - "Unit %s produces power at time %d while off", - unit.name, - t - ) + @error @sprintf("Unit %s produces power at time %d while off", unit.name, t) err_count += 1 end @@ -169,9 +156,7 @@ function _validate_units(instance, solution; tol = 0.01) end # Ramp-up limit - if !is_starting_up && - !is_shutting_down && - (ramp_up > unit.ramp_up_limit + tol) + if !is_starting_up && !is_shutting_down && (ramp_up > unit.ramp_up_limit + tol) @error @sprintf( "Unit %s exceeds ramp up limit at time %d (%.2f > %.2f)", unit.name, @@ -201,7 +186,7 @@ function _validate_units(instance, solution; tol = 0.01) # Calculate how much time the unit has been offline time_down = 0 - for k in 1:(t-1) + for k = 1:(t-1) if !is_on[t-k] time_down += 1 else @@ -235,7 +220,7 @@ function _validate_units(instance, solution; tol = 0.01) # Calculate how much time the unit has been online time_up = 0 - for k in 1:(t-1) + for k = 1:(t-1) if is_on[t-k] time_up += 1 else @@ -288,7 +273,7 @@ end function _validate_reserve_and_demand(instance, solution, tol = 0.01) err_count = 0 - for t in 1:instance.time + for t = 1:instance.time load_curtail = 0 fixed_load = sum(b.load[t] for b in instance.buses) ps_load = 0 @@ -298,13 +283,10 @@ function _validate_reserve_and_demand(instance, solution, tol = 0.01) ps in instance.price_sensitive_loads ) end - production = - sum(solution["Production (MW)"][g.name][t] for g in instance.units) + production = sum(solution["Production (MW)"][g.name][t] for g in instance.units) if "Load curtail (MW)" in keys(solution) - load_curtail = sum( - solution["Load curtail (MW)"][b.name][t] for - b in instance.buses - ) + load_curtail = + sum(solution["Load curtail (MW)"][b.name][t] for b in instance.buses) end balance = fixed_load - load_curtail - production + ps_load @@ -321,12 +303,13 @@ function _validate_reserve_and_demand(instance, solution, tol = 0.01) err_count += 1 end - + # Verify flexiramp solutions only if either of the up-flexiramp and # down-flexiramp requirements is not a default array of zeros - if instance.reserves.upflexiramp != zeros(instance.time) || instance.reserves.dwflexiramp != zeros(instance.time) + if instance.reserves.upflexiramp != zeros(instance.time) || + instance.reserves.dwflexiramp != zeros(instance.time) upflexiramp = - sum(solution["Up-flexiramp (MW)"][g.name][t] for g in instance.units) + sum(solution["Up-flexiramp (MW)"][g.name][t] for g in instance.units) upflexiramp_shortfall = (instance.flexiramp_shortfall_penalty[t] >= 0) ? solution["Up-flexiramp shortfall (MW)"][t] : 0 @@ -341,7 +324,7 @@ function _validate_reserve_and_demand(instance, solution, tol = 0.01) ) err_count += 1 end - + dwflexiramp = sum(solution["Down-flexiramp (MW)"][g.name][t] for g in instance.units) @@ -359,11 +342,10 @@ function _validate_reserve_and_demand(instance, solution, tol = 0.01) ) err_count += 1 end - # Verify spinning reserve solutions only if both up-flexiramp and - # down-flexiramp requirements are arrays of zeros. + # Verify spinning reserve solutions only if both up-flexiramp and + # down-flexiramp requirements are arrays of zeros. else - reserve = - sum(solution["Reserve (MW)"][g.name][t] for g in instance.units) + reserve = sum(solution["Reserve (MW)"][g.name][t] for g in instance.units) reserve_shortfall = (instance.shortfall_penalty[t] >= 0) ? solution["Reserve shortfall (MW)"][t] : 0 @@ -379,7 +361,7 @@ function _validate_reserve_and_demand(instance, solution, tol = 0.01) err_count += 1 end end - + end return err_count diff --git a/test/import/egret_test.jl b/test/import/egret_test.jl index 494067a..b4ecf9d 100644 --- a/test/import/egret_test.jl +++ b/test/import/egret_test.jl @@ -7,9 +7,8 @@ using UnitCommitment basedir = @__DIR__ @testset "read_egret_solution" begin - solution = UnitCommitment.read_egret_solution( - "$basedir/../fixtures/egret_output.json.gz", - ) + solution = + UnitCommitment.read_egret_solution("$basedir/../fixtures/egret_output.json.gz") for attr in ["Is on", "Production (MW)", "Production cost (\$)"] @test attr in keys(solution) @test "115_STEAM_1" in keys(solution[attr]) diff --git a/test/instance/read_test.jl b/test/instance/read_test.jl index 676a808..cf86463 100644 --- a/test/instance/read_test.jl +++ b/test/instance/read_test.jl @@ -19,9 +19,9 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip @test instance.lines[5].target.name == "b5" @test instance.lines[5].reactance ≈ 0.17388 @test instance.lines[5].susceptance ≈ 10.037550333 - @test instance.lines[5].normal_flow_limit == [1e8 for t in 1:4] - @test instance.lines[5].emergency_flow_limit == [1e8 for t in 1:4] - @test instance.lines[5].flow_limit_penalty == [5e3 for t in 1:4] + @test instance.lines[5].normal_flow_limit == [1e8 for t = 1:4] + @test instance.lines[5].emergency_flow_limit == [1e8 for t = 1:4] + @test instance.lines[5].flow_limit_penalty == [5e3 for t = 1:4] @test instance.lines_by_name["l5"].name == "l5" @test instance.lines[1].name == "l1" @@ -29,9 +29,9 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip @test instance.lines[1].target.name == "b2" @test instance.lines[1].reactance ≈ 0.059170 @test instance.lines[1].susceptance ≈ 29.496860773945 - @test instance.lines[1].normal_flow_limit == [300.0 for t in 1:4] - @test instance.lines[1].emergency_flow_limit == [400.0 for t in 1:4] - @test instance.lines[1].flow_limit_penalty == [1e3 for t in 1:4] + @test instance.lines[1].normal_flow_limit == [300.0 for t = 1:4] + @test instance.lines[1].emergency_flow_limit == [400.0 for t = 1:4] + @test instance.lines[1].flow_limit_penalty == [1e3 for t = 1:4] @test instance.buses[9].name == "b9" @test instance.buses[9].load == [35.36638, 33.25495, 31.67138, 31.14353] @@ -44,12 +44,12 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip @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.must_run == [false for t = 1:4] + @test unit.min_power_cost == [1400.0 for t = 1:4] @test unit.min_uptime == 1 @test unit.min_downtime == 1 - @test unit.provides_spinning_reserves == [true for t in 1:4] - for t in 1:1 + @test unit.provides_spinning_reserves == [true for t = 1:4] + for t = 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 @@ -68,7 +68,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip unit = instance.units[2] @test unit.name == "g2" - @test unit.must_run == [false for t in 1:4] + @test unit.must_run == [false for t = 1:4] unit = instance.units[3] @test unit.name == "g3" @@ -77,12 +77,12 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip @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.must_run == [true for t = 1:4] + @test unit.min_power_cost == [0.0 for t = 1:4] @test unit.min_uptime == 1 @test unit.min_downtime == 1 - @test unit.provides_spinning_reserves == [true for t in 1:4] - for t in 1:4 + @test unit.provides_spinning_reserves == [true for t = 1:4] + for t = 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 @@ -101,8 +101,8 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip load = instance.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 load.revenue == [100.0 for t = 1:4] + @test load.demand == [50.0 for t = 1:4] @test instance.price_sensitive_loads_by_name["ps1"].name == "ps1" end diff --git a/test/model/formulations_test.jl b/test/model/formulations_test.jl index 3b08dc5..01277a2 100644 --- a/test/model/formulations_test.jl +++ b/test/model/formulations_test.jl @@ -23,6 +23,7 @@ function _small_test(formulation::Formulation)::Nothing instances = ["matpower/case118/2017-02-01", "test/case14"] for instance in instances # Should not crash + @show "$(instance)" UnitCommitment.build_model( instance = UnitCommitment.read_benchmark(instance), formulation = formulation, @@ -58,17 +59,26 @@ function _test(formulation::Formulation)::Nothing end @testset "formulations" begin + @show "testset formulations" _test(Formulation()) + @show "ArrCon2000 ramping" _test(Formulation(ramping = ArrCon2000.Ramping())) + # _test(Formulation(ramping = DamKucRajAta2016.Ramping())) + @show "MorLatRam2013 ramping" _test( Formulation( ramping = MorLatRam2013.Ramping(), startup_costs = MorLatRam2013.StartupCosts(), ), ) + @show "PanGua2016 ramping" _test(Formulation(ramping = PanGua2016.Ramping())) + @show "Gar1962 PwlCosts" _test(Formulation(pwl_costs = Gar1962.PwlCosts())) + @show "CarArr2006 PwlCosts" _test(Formulation(pwl_costs = CarArr2006.PwlCosts())) + @show "KnuOstWat2018 PwlCosts" _test(Formulation(pwl_costs = KnuOstWat2018.PwlCosts())) + @show "formulations completed" end diff --git a/test/runtests.jl b/test/runtests.jl index 836eda2..66098e3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,6 +11,7 @@ UnitCommitment._setup_logger() const ENABLE_LARGE_TESTS = ("UCJL_LARGE_TESTS" in keys(ENV)) @testset "UnitCommitment" begin + @show "running runtests.jl" include("usage.jl") @testset "import" begin include("import/egret_test.jl") @@ -27,6 +28,7 @@ const ENABLE_LARGE_TESTS = ("UCJL_LARGE_TESTS" in keys(ENV)) include("solution/methods/XavQiuWanThi19/sensitivity_test.jl") end @testset "transform" begin + @show "beginning transform" include("transform/initcond_test.jl") include("transform/slice_test.jl") @testset "randomize" begin diff --git a/test/solution/methods/XavQiuWanThi19/find_test.jl b/test/solution/methods/XavQiuWanThi19/find_test.jl index ddd02c1..57755c5 100644 --- a/test/solution/methods/XavQiuWanThi19/find_test.jl +++ b/test/solution/methods/XavQiuWanThi19/find_test.jl @@ -7,7 +7,7 @@ import UnitCommitment: _Violation, _offer, _query @testset "find_violations" begin instance = UnitCommitment.read_benchmark("test/case14") - for line in instance.lines, t in 1:instance.time + for line in instance.lines, t = 1:instance.time line.normal_flow_limit[t] = 1.0 line.emergency_flow_limit[t] = 1.0 end @@ -20,8 +20,8 @@ import UnitCommitment: _Violation, _offer, _query buses = instance.buses, isf = isf, ) - inj = [1000.0 for b in 1:13, t in 1:instance.time] - overflow = [0.0 for l in instance.lines, t in 1:instance.time] + inj = [1000.0 for b = 1:13, t = 1:instance.time] + overflow = [0.0 for l in instance.lines, t = 1:instance.time] violations = UnitCommitment._find_violations( instance = instance, net_injections = inj, diff --git a/test/transform/initcond_test.jl b/test/transform/initcond_test.jl index 3d891f3..326fa91 100644 --- a/test/transform/initcond_test.jl +++ b/test/transform/initcond_test.jl @@ -8,8 +8,7 @@ basedir = @__DIR__ @testset "generate_initial_conditions!" begin # Load instance - instance = - UnitCommitment.read("$basedir/../fixtures/case118-initcond.json.gz") + instance = UnitCommitment.read("$basedir/../fixtures/case118-initcond.json.gz") optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0) # All units should have unknown initial conditions diff --git a/test/transform/randomize/XavQiuAhm2021_test.jl b/test/transform/randomize/XavQiuAhm2021_test.jl index a3107c4..ff88eb5 100644 --- a/test/transform/randomize/XavQiuAhm2021_test.jl +++ b/test/transform/randomize/XavQiuAhm2021_test.jl @@ -28,10 +28,7 @@ test_approx(x, y) = @test isapprox(x, y, atol = 1e-3) test_approx(bus.load[1] / prev_system_load[1], 0.012) Random.seed!(42) - randomize!( - instance, - XavQiuAhm2021.Randomization(randomize_load_profile = false), - ) + randomize!(instance, XavQiuAhm2021.Randomization(randomize_load_profile = false)) # Check randomized costs test_approx(unit.min_power_cost[1], 831.977) diff --git a/test/transform/slice_test.jl b/test/transform/slice_test.jl index f330bda..69b5fc4 100644 --- a/test/transform/slice_test.jl +++ b/test/transform/slice_test.jl @@ -5,6 +5,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip @testset "slice" begin + @show "beginning slice" instance = UnitCommitment.read_benchmark("test/case14") modified = UnitCommitment.slice(instance, 1:2) @@ -35,7 +36,8 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON, GZip @test length(ps.demand) == 2 @test length(ps.revenue) == 2 end - + @show "beginning building model under slice" + @show instance.reserves # Should be able to build model without errors optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0) model = UnitCommitment.build_model( diff --git a/test/usage.jl b/test/usage.jl index 5ff589d..d9dfad7 100644 --- a/test/usage.jl +++ b/test/usage.jl @@ -6,7 +6,7 @@ using UnitCommitment, LinearAlgebra, Cbc, JuMP, JSON @testset "build_model" begin instance = UnitCommitment.read_benchmark("test/case14") - for line in instance.lines, t in 1:4 + for line in instance.lines, t = 1:4 line.normal_flow_limit[t] = 10.0 end optimizer = optimizer_with_attributes(Cbc.Optimizer, "logLevel" => 0)