Lecture 19
November 6, 2024
Text: VSRIKRISH to 22333
\[\begin{alignat}{3} & \min_{y_{g,t}} && \sum_g VarCost_g \times \sum_t y_{g,t} && \notag\\ & \text{subject to:} && && \notag\\ & && \sum_g y_{g,t} = d_t && \forall t \in T \\ & && y_{g,t} \leq P^{\text{max}}_g && \forall g \in G, t \in T \\ & && y_{g,t} \geq P^{\text{min}}_g && \forall g \in G, t \in T \\ & && y_{g,t+1} - y_{g, t} \leq R_g && \forall g \in G, t \in T \\ & && y_{g,t} - y_{g, t+1} \leq R_g && \forall g \in G, t \in T \end{alignat}\]
Unit commitment extends economic dispatch by also considering whether generating units should be committed, or scheduled to be online.
So:
We want to schedule units to meet demand at lowest cost.
What are our decision variables?
Variable | Definition |
---|---|
\(y_{g,t}\) | power generated by generator \(g\) (MWh) |
\(c_{g,t}\) | commitment status of thermal generator \(g\) at time \(t\) |
\(start_{g,t}\) | startup decision of thermal generator \(g\) at time \(t\) |
\(shut_{g,t}\) | shutdown decision of thermal generator \(g\) at time \(t\) |
\[\begin{aligned} &\min \textcolor{blue}{generation\ cost} + \textcolor{red}{startup\ costs}\\ \Rightarrow &\min \color{blue} \sum_{g \in G, t \in T} VarCost_{g} \times y_{g,t} \color{black} + \\ & \quad \color{red} \sum_{g \in {G_{thermal}}, T \in T} StartCost_g \times start_{g,t} \end{aligned}\]
\[\begin{alignat}{2} & \sum_g y_{g,t} >= d_t && \quad \forall t \in T\\ & y_{g,t} \leq P^{\text{max}}_g \times c_{g,t} && \quad \forall g \in G, t \in T \\ & y_{g,t} \geq P^{\text{min}}_g \times c_{g,t} && \quad \forall g \in G, t \in T \end{alignat}\]
\[\begin{alignat}{2} &c_{g,t} \geq \sum_{s = t - MinUp_g}^t start_{g,s} & \quad \forall g \in G_\text{thermal}, t \in T \\ & 1 - c_{g,t} \geq \sum_{s = t - MinDown_g}^t shut_{g,s} & \quad \forall g \in G_\text{thermal}, t \in T\\ & c_{g, t+1} - c_{g, t} = start_{g, t+1} - shut_{g, t+1} & \quad \forall g \in G_\text{thermal}, t \in T \\ & c_{g, t} = 1 & \quad \forall g \notin G_\text{thermal} \end{alignat}\]
How do we deal with ramp constraints?
Key issue: if we start a generator, we jump from generating 0 MW to at least \(P^\text{min}_g\) MW.
This could violate the ramp limit \(R_g\)!
Solution: Introduce a new variable, \(y^\text{aux}_{g,t}\), which is the generation above the minimum (if commited):
\[y^\text{aux}_{g,t} = y_{g,t} - \left(P^\text{min}_g \times c_{g,t}\right)\]
\[\begin{alignat}{2} & y^\text{aux}_{g,t} = y_{g,t} - \left(P^\text{min}_g \times c_{g,t}\right) && \quad \forall g \in G_\text{thermal}, t \in T \\\\ & y^\text{aux}_{g,t+1} - y^\text{aux}_{g, t} \leq R_g && \quad \forall g \in G_\text{thermal}, t \in T \\\\ & y^\text{aux}_{g,t} - y^\text{aux}_{g, t+1} \leq R_g && \quad \forall g \in G_\text{thermal}, t \in T \end{alignat}\]
cf = DataFrame(CSV.File("data/unit_commitment/gen_variability.csv"))
NY_demand = DataFrame(CSV.File("data/economic_dispatch/2020_hourly_load_NY.csv"))
rename!(NY_demand, :"Time Stamp" => :Date)
d = NY_demand[:, [:Date, :C]]
rename!(d, :C => :Demand)
n = 307 # pick day
T_period = (n*24+1):((n+7)*24)
d = d[T_period, :]
p1 = @df d plot(:Date, :Demand, xlabel="Date", ylabel="Demand (MWh)", label=:false, linewidth=4, xrot=45, bottommargin=24mm)
plot!(p1, size=(600, 600))
display(p1)
p2 = plot(d[!, :Date], cf[!, :Wind], xlabel="Date", ylabel="Capacity Factor (%)", label="Wind", color=:blue, xrot=45, bottommargin=24mm, linewidth=4)
plot!(p2, d[!, :Date], cf[!, :Solar], label="Solar", color=:orange, xrot=45, bottommargin=24mm, linewidth=4)
ylims!(p2, (0, 1))
plot!(p2, size=(600, 600))
display(p2)
h = nrow(d)
T = 1:h
G = 1:nrow(gens)
Gthermal = [1; collect(4:11)]
uc = Model(HiGHS.Optimizer)
@variable(uc, y[g in G, t in T] >= 0) # generation
@variable(uc, c[g in G, t in T], Bin) # commitment
@variable(uc, start[g in Gthermal, t in T], Bin) # startup
@variable(uc, stop[g in Gthermal, t in T], Bin) # shutdown
@variable(uc, yaux[g in Gthermal, t in T] >= 0) # aux generation
@objective(uc, Min, sum(gens.VarCost .* [sum(y[g, :]) for g in G]) +
sum(gens.StartCost[Gthermal] .* [sum(start[g, :]) for g in Gthermal]))
Grenew = G[G .∉ Ref(Gthermal)] # find elements not in Gthermal
@constraint(uc, load[t in T], sum(y[:, t]) >= d.Demand[t])
@constraint(uc, maxgen[g in Gthermal, t in T], y[g, t] <= gens.Pmax[g] * c[g, t] * cf[t, g])
@constraint(uc, renewmax[g in Grenew, t in T], y[g, t] <= gens.Pmax[g] * cf[t, g])
@constraint(uc, mingen[g in G, t in T], y[g, t] >= gens.Pmin[g] * c[g, t])
@constraint(uc, startup[g in Gthermal, t in T],
c[g, t] >= sum(start[g, s] for s in intersect(T, (t - gens.MinUp[g]):t)))
@constraint(uc, shutdown[g in Gthermal, t in T],
1 - c[g, t] >= sum(stop[g, s] for s in intersect(T, (t - gens.MinDown[g]):t)))
@constraint(uc, commit[g in Gthermal, t in 1:(h-1)],
c[g, t+1] - c[g, t] == start[g, t+1] - stop[g, t+1])
@constraint(uc, aux[g in Gthermal, t in T],
yaux[g, t] == y[g, t] - (gens.Pmin[g]) * c[g, t])
@constraint(uc, rampup[g in Gthermal, t in 1:(h-1)],
yaux[g, t + 1] - yaux[g, t] <= gens.Ramp[g])
@constraint(uc, rampdown[g in Gthermal, t in 1:(h-1)],
yaux[g, t] - yaux[g, t + 1] <= gens.Ramp[g])
Unit Commitment: $1.9e6
Economic Dispatch: $2.8e6
Plant | Generation Difference (MW) |
---|---|
Nuclear | 17978.0 |
Biomass | 2535.0 |
Hydroelectric | 308.0 |
NG CCGT1 | 12194.0 |
NG CCGT2 | -3253.0 |
NG CCGT3 | -14270.0 |
Plant | Generation Difference (MW) |
---|---|
NG CT1 | -11235.0 |
NG CT2 | -3582.0 |
NG CT3 | -3560.0 |
NG CT4 | 2205.0 |
NG CT5 | 682.0 |
Monday: Network Models and Solid Waste Management
Wednesday: Prelim (in class)!