|
| 1 | +# # Thermal Generation Dispatch Example |
| 2 | + |
| 3 | +#md # [](@__REPO_ROOT_URL__/examples/Thermal_Generation_Dispatch_Example.jl) |
| 4 | + |
| 5 | +# This example illustrates the sensitivity analysis of thermal generation dispatch problem. |
| 6 | + |
| 7 | +# This problem can be described as the choice of thermal generation `g` given a demand `d`, a price for thermal generation `c` and a penalty price `c_{ϕ}` for any demand not attended ϕ. |
| 8 | + |
| 9 | +# ```math |
| 10 | +# \begin{split} |
| 11 | +# \begin{array} {ll} |
| 12 | +# \mbox{minimize} & \sum_{i=1}^{N} c_{i} g_{i} + c_{\phi} \phi \\ |
| 13 | +# \mbox{s.t.} & g_{i} \ge 0 \quad i=1..N \\ |
| 14 | +# & g_{i} \le G_{i} \quad i=1..N \\ |
| 15 | +# & \sum_{i=1}^{N} g_{i} + \phi = d\\ |
| 16 | +# \end{array} |
| 17 | +# \end{split} |
| 18 | +# ``` |
| 19 | +# where |
| 20 | +# - `G_{i}` is the maximum possible generation for a thermal generator `i` |
| 21 | + |
| 22 | +# ## Define and solve the Thermal Dispatch Problem |
| 23 | + |
| 24 | +# First, import the libraries. |
| 25 | + |
| 26 | +using Test |
| 27 | +using JuMP |
| 28 | +import DiffOpt |
| 29 | +import LinearAlgebra: dot |
| 30 | +import HiGHS |
| 31 | +import MathOptInterface as MOI |
| 32 | +import Plots |
| 33 | + |
| 34 | +# Define the model that will be construct given a set of parameters. |
| 35 | + |
| 36 | +function generate_model( |
| 37 | + d_data::Float64; |
| 38 | + g_sup::Vector{Float64}, |
| 39 | + c_g::Vector{Float64}, |
| 40 | + c_ϕ::Float64, |
| 41 | +) |
| 42 | + ## Creation of the Model and Parameters |
| 43 | + model = DiffOpt.quadratic_diff_model(HiGHS.Optimizer) |
| 44 | + set_silent(model) |
| 45 | + I = length(g_sup) |
| 46 | + |
| 47 | + ## Variables |
| 48 | + @variable(model, g[i in 1:I] >= 0.0) |
| 49 | + @variable(model, ϕ >= 0.0) |
| 50 | + |
| 51 | + ## Parameters |
| 52 | + @variable(model, d in Parameter(d_data)) |
| 53 | + |
| 54 | + ## Constraints |
| 55 | + @constraint(model, limit_constraints_sup[i in 1:I], g[i] <= g_sup[i]) |
| 56 | + @constraint(model, demand_constraint, sum(g) + ϕ == d) |
| 57 | + |
| 58 | + ## Objectives |
| 59 | + @objective(model, Min, dot(c_g, g) + c_ϕ * ϕ) |
| 60 | + |
| 61 | + ## Solve the model |
| 62 | + optimize!(model) |
| 63 | + |
| 64 | + ## Return the solved model |
| 65 | + return model |
| 66 | +end |
| 67 | + |
| 68 | +# Define the functions that will get the primal values `g` and `\phi` and sensitivity analysis of the demand `dg/dd` and `d\phi/dd` from a optimized model. |
| 69 | + |
| 70 | +function diff_forward(model::Model, ϵ::Float64 = 1.0) |
| 71 | + ## Initialization of parameters and references to simplify the notation |
| 72 | + vect_ref = [model[:g]; model[:ϕ]] |
| 73 | + |
| 74 | + ## Get the primal solution of the model |
| 75 | + vect = value.(vect_ref) |
| 76 | + |
| 77 | + ## Reset the sensitivities of the model |
| 78 | + DiffOpt.empty_input_sensitivities!(model) |
| 79 | + |
| 80 | + ## Pass the perturbation to the DiffOpt Framework |
| 81 | + DiffOpt.set_forward_parameter(model, model[:d], ϵ) |
| 82 | + |
| 83 | + ## Compute the derivatives with the Forward Mode |
| 84 | + DiffOpt.forward_differentiate!(model) |
| 85 | + |
| 86 | + ## Get the derivative of the model |
| 87 | + dvect = DiffOpt.get_forward_variable.(model, vect_ref) |
| 88 | + |
| 89 | + ## Return the values as a vector |
| 90 | + return [vect; dvect] |
| 91 | +end |
| 92 | + |
| 93 | +function diff_reverse(model::Model, ϵ::Float64 = 1.0) |
| 94 | + ## Initialization of parameters and references to simplify the notation |
| 95 | + vect_ref = [model[:g]; model[:ϕ]] |
| 96 | + |
| 97 | + ## Get the primal solution of the model |
| 98 | + vect = value.(vect_ref) |
| 99 | + |
| 100 | + ## Set variables needed for the DiffOpt Backward Framework |
| 101 | + dvect = Array{Float64,1}(undef, length(vect_ref)) |
| 102 | + |
| 103 | + ## Loop for each primal variable |
| 104 | + for i in 1:I+1 |
| 105 | + ## Reset the sensitivities of the model |
| 106 | + DiffOpt.empty_input_sensitivities!(model) |
| 107 | + |
| 108 | + ## Pass the perturbation to the DiffOpt Framework |
| 109 | + DiffOpt.set_reverse_variable.(model, vect_ref[i], ϵ) |
| 110 | + |
| 111 | + ## Compute the derivatives with the Forward Mode |
| 112 | + DiffOpt.reverse_differentiate!(model) |
| 113 | + |
| 114 | + ## Get the derivative of the model |
| 115 | + dvect[i] = DiffOpt.get_reverse_parameter(model, model[:d]) |
| 116 | + end |
| 117 | + |
| 118 | + ## Return the values as a vector |
| 119 | + return [vect; dvect] |
| 120 | +end |
| 121 | + |
| 122 | +# Initialize of Parameters |
| 123 | + |
| 124 | +g_sup = [10.0, 20.0, 30.0] |
| 125 | +I = length(g_sup) |
| 126 | +d = 0.0:0.1:80 |
| 127 | +d_size = length(d) |
| 128 | +c_g = [1.0, 3.0, 5.0] |
| 129 | +c_ϕ = 10.0; |
| 130 | + |
| 131 | +# Generate models for each demand `d` |
| 132 | +models = generate_model.(d; g_sup = g_sup, c_g = c_g, c_ϕ = c_ϕ); |
| 133 | + |
| 134 | +# Get the results of models with the DiffOpt Forward and Backward context |
| 135 | + |
| 136 | +result_forward = diff_forward.(models) |
| 137 | +optimize!.(models) |
| 138 | +result_reverse = diff_reverse.(models); |
| 139 | + |
| 140 | +# Organization of results to plot |
| 141 | +# Initialize data_results that will contain every result |
| 142 | +data_results = Array{Float64,3}(undef, 2, d_size, 2 * (I + 1)); |
| 143 | + |
| 144 | +# Populate the data_results array |
| 145 | +for k in 1:d_size |
| 146 | + data_results[1, k, :] = result_forward[k] |
| 147 | + data_results[2, k, :] = result_reverse[k] |
| 148 | +end |
| 149 | + |
| 150 | +# ## Results with Plot graphs |
| 151 | +# ### Results for the forward context |
| 152 | +# Result Primal Values: |
| 153 | +Plots.plot( |
| 154 | + d, |
| 155 | + data_results[1, :, 1:I+1]; |
| 156 | + title = "Generation by Demand", |
| 157 | + label = ["Thermal Generation 1" "Thermal Generation 2" "Thermal Generation 3" "Generation Deficit"], |
| 158 | + xlabel = "Demand [unit]", |
| 159 | + ylabel = "Generation [unit]", |
| 160 | +) |
| 161 | + |
| 162 | +# Result Sensitivity Analysis: |
| 163 | +Plots.plot( |
| 164 | + d, |
| 165 | + data_results[1, :, I+2:2*(I+1)]; |
| 166 | + title = "Sensitivity of Generation by Demand", |
| 167 | + label = ["T. Gen. 1 Sensitivity" "T. Gen. 2 Sensitivity" "T. Gen. 3 Sensitivity" "Gen. Deficit Sensitivity"], |
| 168 | + xlabel = "Demand [unit]", |
| 169 | + ylabel = "Sensitivity [-]", |
| 170 | +) |
| 171 | + |
| 172 | +# ### Results for the reverse context |
| 173 | +# Result Primal Values: |
| 174 | +Plots.plot( |
| 175 | + d, |
| 176 | + data_results[2, :, 1:I+1]; |
| 177 | + title = "Generation by Demand", |
| 178 | + label = ["Thermal Generation 1" "Thermal Generation 2" "Thermal Generation 3" "Generation Deficit"], |
| 179 | + xlabel = "Demand [unit]", |
| 180 | + ylabel = "Generation [unit]", |
| 181 | +) |
| 182 | + |
| 183 | +# Result Sensitivity Analysis: |
| 184 | +Plots.plot( |
| 185 | + d, |
| 186 | + data_results[2, :, I+2:2*(I+1)]; |
| 187 | + title = "Sensitivity of Generation by Demand", |
| 188 | + label = ["T. Gen. 1 Sensitivity" "T. Gen. 2 Sensitivity" "T. Gen. 3 Sensitivity" "Gen. Deficit Sensitivity"], |
| 189 | + xlabel = "Demand [unit]", |
| 190 | + ylabel = "Sensitivity [-]", |
| 191 | +) |
0 commit comments