Unit Commitment


Lecture 19

November 6, 2024

Review and Questions

Questions

Poll Everywhere QR Code

Text: VSRIKRISH to 22333

URL: https://pollev.com/vsrikrish

See Results

Unit Commitment

Power Systems Decision Problems

Decision Problems for Power Systems by Time Scale

Adapted from Perez-Arriaga, Ignacio J., Hugh Rudnick, and Michel Rivier (2009)

Economic Dispatch

\[\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}\]

What Is Missing From Economic Dispatch?

  • Operational Status
  • Startup Costs
  • Minimum up/down times

Unit Commitment

Unit commitment extends economic dispatch by also considering whether generating units should be committed, or scheduled to be online.

Unit Commitment

So:

  • Given the load profile for the decision period;
  • Given a set of units available

We want to schedule units to meet demand at lowest cost.

Unit Commitment Decision Variables

What are our decision variables?

Unit Commitment 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\)

Unit Commitment Objective

\[\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}\]

Unit Commitment Constraints (Demand/Min/Max Gen)

\[\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}\]

Unit Commitment Constraints (Startup/Shutdown)

\[\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}\]

Unit Commitment Ramp Constraints

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\)!

Unit Commitment Ramp Constraints

Figure 1

Auxiliary Variables

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)\]

Auxiliary Variables

Figure 2

Back to Constraints

\[\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}\]

Unit Commitment Example

Generator Data

  • 1 nuclear, 750 MW capacity, $2/MWh, 20% ramp, 24 hour start/shut time, $50,000 startup cost
  • 1 biomass, 50 MW capacity, $5/MWh, no ramp or startup/shutdown limits
  • 1 hydroelectric, 250 MW capacity, $0/MWh, no ramp or startup/shutdown limits
  • 3 natural gas CCGT, 25-220 MW minimum, 50-500 MW capacity $22-36/MWh, 40% ramp, 6 hour start/shut time, $30,000 startup cost
  • 5 natural gas CT, 0-73 MW minimum, 48-100 MW capacity, $38-46/MWh, 100% ramp, 1 hour start/shut time, $8,000 startup cost

Demand and Capacity Factors

Code
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)
(a) Demand and Capacity Factors.
(b)
Figure 3

Unit Commitment Formulation (JuMP)

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]))

Unit Commitment Formulation (JuMP)

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])

Unit Commitment Formulation (JuMP)

@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 Solution

Code
set_silent(uc)
optimize!(uc)
gen = value.(y).data 
p = areaplot(gen', 
    label=permutedims(gens[:, :Plant]), 
    xlabel = "Hour", 
    ylabel ="Generated Electricity (MW)", 
    color_palette=:tab20,
    grid=:false
)
plot!(legend=:outerright, legendcolumns=1)
plot!(p, size=(1200, 500))
Figure 4: Unit commitment example solution

Comparison with Economic Dispatch (System Cost)

Unit Commitment: $1.9e6

Economic Dispatch: $2.8e6

Comparison with Economic Dispatch (Generation)

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

Comparison with Economic Dispatch (Generation)

Plant Generation Difference (MW)
NG CT1 -11235.0
NG CT2 -3582.0
NG CT3 -3560.0
NG CT4 2205.0
NG CT5 682.0

Key Takeaways

Key Takeaways

  • Unit commitment involves telling generating units whether to operate or not in a given time period.
  • Decision-making about turning plants on and off turns this into a mixed integer program and introduces new constraints for thermal plants.
  • Need to be careful about formulation when introducing integer constraints.

Upcoming Schedule

Next Classes

Monday: Network Models and Solid Waste Management

Wednesday: Prelim (in class)!