From b71a1c3d5f09f2973a2225e66bd9e898185acffd Mon Sep 17 00:00:00 2001 From: Jun He Date: Fri, 7 Apr 2023 16:42:03 -0400 Subject: [PATCH] Updated randomize, validate and initial conditions --- src/transform/initcond.jl | 14 +++++- src/transform/randomize/XavQiuAhm2021.jl | 4 ++ src/validation/validate.jl | 46 +++++++++++++++++-- .../transform/randomize/XavQiuAhm2021_test.jl | 18 ++++++++ 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/src/transform/initcond.jl b/src/transform/initcond.jl index 697d4fd..d0e2954 100644 --- a/src/transform/initcond.jl +++ b/src/transform/initcond.jl @@ -17,24 +17,29 @@ function generate_initial_conditions!( )::Nothing G = sc.thermal_units B = sc.buses + PU = sc.profiled_units t = 1 mip = JuMP.Model(optimizer) # Decision variables @variable(mip, x[G], Bin) @variable(mip, p[G] >= 0) + @variable(mip, pu[PU]) # Constraint: Minimum power @constraint(mip, min_power[g in G], p[g] >= g.min_power[t] * x[g]) + @constraint(mip, pu_min_power[k in PU], pu[k] >= k.min_power[t]) # Constraint: Maximum power @constraint(mip, max_power[g in G], p[g] <= g.max_power[t] * x[g]) + @constraint(mip, pu_max_power[k in PU], pu[k] <= k.capacity[t]) # Constraint: Production equals demand @constraint( mip, power_balance, - sum(b.load[t] for b in B) == sum(p[g] for g in G) + sum(b.load[t] for b in B) == + sum(p[g] for g in G) + sum(pu[k] for k in PU) ) # Constraint: Must run @@ -58,7 +63,12 @@ function generate_initial_conditions!( return c / mw end end - @objective(mip, Min, sum(p[g] * cost_slope(g) for g in G)) + @objective( + mip, + Min, + sum(p[g] * cost_slope(g) for g in G) + + sum(pu[k] * k.cost[t] for k in PU) + ) JuMP.optimize!(mip) diff --git a/src/transform/randomize/XavQiuAhm2021.jl b/src/transform/randomize/XavQiuAhm2021.jl index 630c7f0..f58d9cc 100644 --- a/src/transform/randomize/XavQiuAhm2021.jl +++ b/src/transform/randomize/XavQiuAhm2021.jl @@ -133,6 +133,10 @@ function _randomize_costs( s.cost *= α end end + for pu in sc.profiled_units + α = rand(rng, distribution) + pu.cost *= α + end return end diff --git a/src/validation/validate.jl b/src/validation/validate.jl index 5447980..46c049a 100644 --- a/src/validation/validate.jl +++ b/src/validation/validate.jl @@ -305,6 +305,35 @@ function _validate_units(instance::UnitCommitmentInstance, solution; tol = 0.01) end end end + for pu in sc.profiled_units + production = solution[sc.name]["Profiled production (MW)"][pu.name] + + for t in 1:instance.time + # Unit must produce at least its minimum power + if production[t] < pu.min_power[t] - tol + @error @sprintf( + "Profiled unit %s produces below its minimum limit at time %d (%.2f < %.2f)", + pu.name, + t, + production[t], + pu.min_power[t] + ) + err_count += 1 + end + + # Unit must produce at most its maximum power + if production[t] > pu.capacity[t] + tol + @error @sprintf( + "Profiled unit %s produces above its maximum limit at time %d (%.2f > %.2f)", + pu.name, + t, + production[t], + pu.capacity[t] + ) + err_count += 1 + end + end + end end return err_count end @@ -316,16 +345,25 @@ function _validate_reserve_and_demand(instance, solution, tol = 0.01) load_curtail = 0 fixed_load = sum(b.load[t] for b in sc.buses) ps_load = 0 + production = 0 if length(sc.price_sensitive_loads) > 0 ps_load = sum( solution[sc.name]["Price-sensitive loads (MW)"][ps.name][t] for ps in sc.price_sensitive_loads ) end - production = sum( - solution[sc.name]["Thermal production (MW)"][g.name][t] for - g in sc.thermal_units - ) + if length(sc.thermal_units) > 0 + production = sum( + solution[sc.name]["Thermal production (MW)"][g.name][t] + for g in sc.thermal_units + ) + end + if length(sc.profiled_units) > 0 + production += sum( + solution[sc.name]["Profiled production (MW)"][pu.name][t] + for pu in sc.profiled_units + ) + end if "Load curtail (MW)" in keys(solution) load_curtail = sum( solution[sc.name]["Load curtail (MW)"][b.name][t] for diff --git a/test/transform/randomize/XavQiuAhm2021_test.jl b/test/transform/randomize/XavQiuAhm2021_test.jl index 2c1f473..ea85000 100644 --- a/test/transform/randomize/XavQiuAhm2021_test.jl +++ b/test/transform/randomize/XavQiuAhm2021_test.jl @@ -62,4 +62,22 @@ test_approx(x, y) = @test isapprox(x, y, atol = 1e-3) @test round.(system_load(sc), digits = 1)[1:8] ≈ [4854.7, 4849.2, 4732.7, 4848.2, 4948.4, 5231.1, 5874.8, 5934.8] end + + @testset "profiled unit cost" begin + sc = + UnitCommitment.read("$FIXTURES/case14-profiled.json.gz").scenarios[1] + # Check original costs + pu1 = sc.profiled_units[1] + pu2 = sc.profiled_units[2] + test_approx(pu1.cost[1], 100.0) + test_approx(pu2.cost[1], 50.0) + randomize!( + sc, + XavQiuAhm2021.Randomization(randomize_load_profile = false), + rng = MersenneTwister(42), + ) + # Check randomized costs + test_approx(pu1.cost[1], 98.039) + test_approx(pu2.cost[1], 48.385) + end end