Unit Commitment
Definition
The Unit Commitment (UC) module in KPG Platform provides a framework for scheduling and operating power plants to reliably meet electricity demand at the lowest possible cost. Unlike simple economic dispatch, UC explicitly accounts for generator on/off decisions, minimum up/down times, start-up costs, and ramping limits. By modeling these operational realities, UC helps illustrate how different generation resources are coordinated over time to balance supply and demand while maintaining system reliability.
Mathematical formulation 🧮
Equation explanation
(1a): Objective function
This function minimizes the total operation cost over all generators in set
It includes generation cost
(1b): Minimum Generation Limit Constraint
This constraint ensures that if a generator is online
(1c): Maximum Generation Limit Constraint with Startup & Shutdown Adjustment
This constraint restricts the generator’s net output plus upward reserve from exceeding the maximum capacity.
It also adjusts the upper bound depending on whether the unit is starting up
(1d): Ramp-Up Constraint
This constraint limits the increase in generator output (including upward reserve) between two consecutive time periods to not exceed the generator’s ramp-up capability
(1e): Ramp-down Constraint
This constraint restricts the decrease in generator output (including downward reserve) to remain within the ramp-down capability
(1f): Minimum Up-Time Constraint
Once a unit has been turned on, it must remain online for at least
This constraint prevents the unit from shutting down too soon after startup.
(1g): Minimum Down-Time Constraint
Once a unit is shut down, it must remain offline for at least
This constraint ensures sufficient recovery time before restarting.
(1h): Logical Contraint
This constraint guarantees that
(1i): System Upward Reserve Requirement
This constraint ensures that the system-wide upward reserve requirement
(1j): System Downward Reserve Requirement
This constraint guarantees that the system-wide downward reserve requirement
(1k): Generation-load Balance Contraint
This constraint enforces that total generation must equal total demand across all buses
Julia code 🔢
# Data for the problemgenset = 1:3
T = 6timeset = 1:T
# Costs
CU = [800, 500, 250] # Startup costCD = [200, 300, 400] # Shutdown costC0 = [0 for i in genset]C1 = [5 15 30] # Linear generation costC2 = [0 for i in genset]
# Unit 1: most expensive CU and cheapest C1# Unit 2: moderate CU and C1# Unit 3: Cheapest CU and most expensive C1
# The intuition here is that the UC problem will use unit 1 first, then unit 2, and finally unit 3
# Generation limits (Unit 1 > Unit 2 > Unit 3)Pmin = [80, 50, 30]Pmax = [300, 200, 100]
# Start-up and shutdown ramp rate (Unit 1 > Unit 2 > Unit 3)SU = [100, 70, 40]SD = [80, 50, 30]
# Ramp-up and ramp-down limits (Unit 3 > Unit 2 > Unit 1)RU = [50, 60, 70]RD = [30, 40, 50]
# Minimum up- and down-timeTU = [3, 2, 1]TD = [2, 2, 2]
# Initial state# Unit 1 has to be turned on for 2 hours from its initial stateu0 = [1, 0, 0]P0 = [120, 0, 0]TU0 = [2, 0, 0]TD0 = [0, 0, 0]
# Demand and fixed reserve requirementD = [220, 250, 200, 170, 230, 190]R = [10 for i in timeset]
# Optimization modelusing JuMP, HiGHSm = Model(HiGHS.Optimizer)
# Variables@variables(m, begin p[i in genset, t in timeset] >= 0 # Power generation variable u[i in genset, t in timeset], Bin # Binary variable for on/off status v[i in genset, t in timeset], Bin # startup variable w[i in genset, t in timeset], Bin # shutdown variable r_up[i in genset, t in timeset] >= 0 # upward reserve r_dn[i in genset, t in timeset] >= 0 # downward reserveend)
@constraint(m, MinGen[i in genset, t in timeset], p[i,t] - r_dn[i,t] >= Pmin[i] * u[i,t])
@constraint(m, MaxGen[i in genset, t in timeset], p[i,t] + r_up[i,t] <= Pmax[i] * u[i,t] - (Pmax[i] - SU[i]) * v[i,t] - (Pmax[i] - SD[i]) * w[i,t])
@constraint(m, RampUp[i in genset, t in 2:T], p[i,t] + r_up[i,t] - p[i,t-1] <= RU[i])
@constraint(m, RampDown[i in genset, t in 2:T], p[i,t-1] - (p[i,t] - r_dn[i,t]) <= RD[i])
@constraint(m, MinUpTime[i in genset, t in TU[i]:T], sum(v[i,τ] for τ in t-TU[i]+1:t) <= u[i,t])
@constraint(m, MinDownTime[i in genset, t in TD[i]:T], sum(w[i,τ] for τ in t-TD[i]+1:t) <= 1 - u[i,t])
@constraint(m, Logic[i in genset, t in timeset], u[i,t] - (t > 1 ? u[i,t-1] : u0[i]) == v[i,t] - w[i,t])
@constraint(m, UpReserve[t in timeset], ㅌsum(r_up[i,t] for i in genset) >= R[t])
@constraint(m, DownReserve[t in timeset], sum(r_dn[i,t] for i in genset) >= R[t])
@constraint(m, LoadBalance[t in timeset], sum(p[i,t] for i in genset) == D[t])
# Objective function@objective(m, Min, sum(C1[i] * p[i,t] + CU[i] * v[i,t] + CD[i] * w[i,t] for i in genset, t in timeset))
optimize!(m)
#----------------------------------------------------------------------------------#
println("Termination status: ", termination_status(m))println("Objective value: ", objective_value(m))println("P: ", value.(P))println("u: ", value.(u))println("Gencost: ", value.(Gencost))println("cU: ", value.(cU))println("OC: ", value.(OC))⸻