Shadow Prices and Power Systems Examples


Lecture 12

October 20, 2024

Review

Last Class

  • Decision Models include:
    • Decision Variables
    • Objectives
    • Constraints
  • Objectives vs. Metrics: “Minimize cost” versus the cost function.

Linear Programming

Linear Programs (LPs) are:

  • Linear
  • Divisible
  • Certain

LPs must have optimal solutions at intersections of constraints: simplex method.

Questions?

Poll Everywhere QR Code

Text: VSRIKRISH to 22333

URL: https://pollev.com/vsrikrish

See Results

Linearization and Shadow Prices

Linearization

Linear models come up frequently because we can linearize nonlinear functions.

When we linearize components of an mathematical program, this is called the linear relaxation of the original problem.

Code
E = 0:0.01:1
plot(E, E.^2, legend=false, grid=false, xlabel="Efficiency", ylabel="Cost", color=:black, yticks=false, xlims=(0, 1), ylims=(0, 1), left_margin=8mm, linewidth=3)
xticks!([0.65, 0.95])
xlims!((0, 1.05))
scatter!([0.65, 0.95], [0.65, 0.95].^2, markersize=10, color=:blue)
plot!(E, 1.6 .* E  .- 0.6175, color=:blue, linestyle=:dash, linewidth=3)
plot!(size=(600, 500))

Sensitivity of Linearization

The quality of linear approximations can depend on the range of values used and the curvature of the original objective/constraint.

Code
scatter!([0.32, 0.98], [0.32, 0.98].^2, markersize=10, color=:red)
plot!(E, 1.3 .* E  .- 0.31, color=:red, linestyle=:dot, linewidth=3)

Shadow Prices

Binding Constraints

A solution will be found at one of the corner points of the feasible polytope.

This means that at this solution, one or more constraints are binding: if we relaxed the constraint by weakening it, we could improve the solution.

Binding Constraints

Code
x1 = 0:1200
x2 = 0:1400
f1(x) = (600 .- 0.9 .* x) ./ 0.5
f2(x) = 1000 .- x

p = plot(0:667, min.(f1(0:667), f2(0:667)), fillrange=0, color=:lightblue, grid=true, label="Feasible Region", xlabel=L"x_1", ylabel=L"x_2", xlims=(-50, 1200), ylims=(-50, 1400), framestyle=:origin, minorticks=4, right_margin=4mm, left_margin=4mm)
plot!(0:667, f1.(0:667), color=:brown, linewidth=3, label=false)
plot!(0:1000, f2.(0:1000), color=:red, linewidth=3, label=false)
annotate!(400, 1100, text(L"0.9x_1 + 0.5x_2 = 600", color=:purple, pointsize=18))
annotate!(1000, 300, text(L"x_1 + x_2 = 1000", color=:red, pointsize=18))
plot!(size=(600, 500))

Z(x1,x2) = 230 * x1 + 120 * x2
contour!(0:660,0:1000,(x1,x2)->Z(x1,x2), levels=5, c=:devon, linewidth=2, colorbar = false, clabels = true) 

scatter!(p, [667], [0], markersize=10, z=2, label="Optimal Solution", markercolor=:orange)

The binding constraints are

  • \(x_1 \geq 0\)
  • \(0.9 x_1 + 0.5 x_2 \leq 600\),

but not

  • \(x_1 + x_2 \leq 1000\).

Shadow Prices

The marginal cost of a constraint is the amount by which the solution would improve if the constraint capacity was relaxed by one unit.

This is also referred to as the shadow price (these are also called the dual variables of the constraint).

Non-zero shadow prices tell us that the constraint is binding, and their values rank which constraints are most influential.

Reminder: Constrained Optimization and Lagrange Multipliers

Lagrange Multipliers are a way to incorporate equality constraints into an “unconstrained” form for an optimization problem.

Reminder: Constrained Optimization and Lagrange Multipliers

\[\begin{align*} \max_{x,y}\qquad &f(x,y) \\ \text{subject to:}\qquad &g(x,y) = 0 \end{align*}\]

Then: \[\mathcal{L}(x, y, \lambda) = f(x,y) - \lambda g(x,y)\]

Converting Inequality Constraints

Original Problem

\[ \begin{aligned} & \min &&f(x_1, x_2) \notag \\\\ & \text{subject to:} && x_1 \geq A \notag \\ & && x_2 \leq B \notag \end{aligned} \]

With Dummy Variables

\[ \begin{aligned} & \min &&f(x_1, x_2) \notag \\\\ & \text{subject to:} && x_1 - S_1^2 = A \notag \\ & && x_2 + S_2^2 = B \notag \end{aligned} \]

Using Lagrange Multipliers

Then the Lagrangian function becomes:

\[ \mathcal{L}(\mathbf{x}, S_1, S_2, \lambda_1, \lambda_2) = f(\mathbf{x}) - \lambda_1(x_1 - S_1^2 - A) - \lambda_2(x_2 + S_2^2 - B) \]

where \(\lambda_1\), \(\lambda_2\) are penalties for violating the constraints.

The \(\lambda_i\) are the eponymous Lagrange multipliers.

Using Lagrange Multipliers

Next step: locate possible optima where the partial derivatives of the Lagrangian are zero.

\[\frac{\partial \mathcal{L}(\cdot)}{\partial \cdot} = 0\]

This is actually many equations, even though our original problem was low-dimensional, and can be slow to solve.

Shadow Prices are Lagrange Multipliers

The shadow prices are the Lagrange Multipliers of the optimization problem.

If our inequality constraints \(X \geq A\) and \(X \leq B\) are written as \(X - S_1^2 = A\) and \(X + S_2^2 = B\):

\[\mathcal{L}(X, S_1 S_2, \lambda_1, \lambda_2) = Z(X) - \lambda_1(X - S_1^2 - A) - \lambda_2(X + S_2^2 - B),\]

\[\Rightarrow \qquad \frac{\partial \mathcal{L}}{\partial A} = \lambda_1, \qquad \frac{\partial \mathcal{L}}{\partial B} = \lambda_2.\]

Electric Power System Decision Problems

Overview of Electric Power Systems

Power Systems Schematic

Source: Wikipedia

Decisions Problems for Power Systems

Decision Problems for Power Systems by Time Scale

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

Electricity Generation by Source

Electricity Generation in 2024 by Source

Generating Capacity Expansion

Capacity Expansion

Capacity expansion involves adding resources to generate or transmit electricity to meet anticipated demand (load) in the future.

Typical objective: Minimize cost

But other constraints may apply, e.g. reducing CO2 emissions or increasing fuel diversity.

Simple Capacity Expansion Example

Plant Types

In general, we have many fuel options:

  • Gas (combined cycle or simple cycle);
  • Coal;
  • Nuclear;
  • Renewables (wind, solar, hydro, geothermal)

Simplified Example: Generators

Code
gens = DataFrame(CSV.File("data/capacity_expansion/generators.csv"))
gens_display = rename(gens, Symbol.([:Generator, :"Fixed Cost (\\\$)", :"Variable Cost (\\\$/MW)"]))
markdown_table(gens_display[1:end-2, :])
Generator Fixed Cost ($) Variable Cost ($/MW)
Geothermal 450000 0
Coal 220000 21
NG CCGT 82000 25
NG CT 65000 35

Simplified Example: Demand

Code
NY_demand = DataFrame(CSV.File("data/capacity_expansion/2020_hourly_load_NY.csv"))
rename!(NY_demand, :"Time Stamp" => :Date)
demand = NY_demand[:, [:Date, :C]]
rename!(demand, :C => :Demand)
@df demand plot(:Date, :Demand, xlabel="Date", ylabel="Demand (MWh)", label=:false, xrot=45, bottom_margin=15mm)
plot!(size=(1200, 500))
Figure 1: Demand for 2020 in NYISO Zone C

Load Duration Curves

The chronological demand curve makes it hard to understand what levels of load occur with lower or greater frequency.

Instead, we can sort demand from high to low, which creates a load duration curve.

Simplified Example: Load Duration Curve

Code
plot(sort(demand.Demand, rev=true), linewidth=3, label=:false)
xaxis!("Hours")
yaxis!("Demand (MWh)")
plot!(size=(1200, 450))
Figure 2: Load Duration Curve for 2020 in NYISO Zone C

Simplified Example: Load Duration Curve

Code
hline!([2400], color=:red, linewidth=3, label=false)
annotate!(2000, 2500, text("Peak Load", :red, :right, 20))
hline!([1500], color=:brown, linewidth=3, label=false)
annotate!(4000, 2000, text("Shoulder Load", :brown, :left, 20))
annotate!(2000, 1400, text("Base Load", :blue, :right, 20))
Figure 3: Load Duration Curve for 2020 in NYISO Zone C

Capacity Expansion: Goal

We want to find the installed capacity of each technology that meets demand at all hours at minimal total cost.

Although…

  • For some hours with extreme load, is it necessarily worth it to build new generation to meet those peaks?
  • Instead, assign a high cost \(NSECost\) to non-served energy (NSE). In this case, let’s set \(NSECost = \$9000\)/MWh

Decision Variables

What are our variables?

Variable Meaning
\(x_g\) installed capacity (MW) from each generator type \(g \in \mathcal{G}\)
\(y_{g,t}\) production (MWh) from each generator type \(g\) in hour \(t \in \mathcal{T}\)
\(NSE_t\) non-served energy (MWh) in hour \(t \in \mathcal{T}\)

Capacity Expansion Objective

What is our objective?

\[ \begin{align} \min_{x, y, NSE} Z &= {\color{red}\text{investment cost} } + {\color{blue}\text{operating cost} } \\ &= {\color{red} \sum_{g \in \mathcal{G}} \times \text{FixedCost}_g x_g} + \\ & \qquad{\color{blue} \sum_{t \in \mathcal{T}} \sum_{g \in \mathcal{G}} \text{VarCost}_g \times y_{g,t} + \sum_{t \in \mathcal{T}} \text{NSECost} \times NSE_t} \end{align} \]

What Are Our Constraints?

Capacity Expansion Constraints

  • Demand: Sum of generated energy and non-served energy must equal demand \(d_t\).
  • Capacity: Generator types cannot produce more electricity than their installed capacity.
  • Non-negativity: All decision variables must be non-negative.

Problem Formulation

\[ \begin{align} \min_{x, y, NSE} \quad & \sum_{g \in \mathcal{G}} \text{FixedCost}_g \times x_g + \sum_{t \in \mathcal{T}} \sum_{g \in \mathcal{G}} \text{VarCost}_g \times y_{g,t} & \\ & \quad + \sum_{t \in \mathcal{T}} \text{NSECost} \times NSE_t & \\[0.5em] \text {subject to:} \quad & \sum_{g \in \mathcal{G}} y_{g,t} + NSE_t \geq d_t \qquad \forall t \in \mathcal{T} \\[0.5em] & y_{g,t} \leq x_g \qquad \qquad \qquad\qquad \forall g \in {G}, \forall t \in \mathcal{T} \\[0.5em] & x_g, y_{g,t}, NSE_t \geq 0 \qquad \qquad \forall g \in {G}, \forall t \in \mathcal{T} \end{align} \]

Capacity Expansion is an LP

  • Linearity: costs assumed to scale linearly;
  • Divisible: we model total installed capacity, not number of individual generator units
  • Certainty: no uncertainty about renewables.

Real problems can get much more complex, particularly if we try to model making decisions under renewable or load uncertainty.

Implementation in JUMP.jl

# define sets
G = 1:nrow(gens[1:end-2, :])
T = 1:nrow(demand)
NSECost = 9000

gencap = Model(HiGHS.Optimizer)
# define variables
@variables(gencap, begin
    x[g in G] >= 0
    y[g in G, t in T] >= 0
    NSE[t in T] >= 0
end)
@objective(gencap, Min, 
    sum(gens[G, :FixedCost] .* x) + sum(gens[G, :VarCost] .* sum(y[:, t] for t in T)) + NSECost * sum(NSE)
)
@constraint(gencap, load[t in T], sum(y[:, t]) + NSE[t] >= demand.Demand[t])
@constraint(gencap, availability[g in G, t in T], y[g, t] <= x[g])
optimize!(gencap)

Key Takeaways

Key Takeaways

  • Shadow prices (dual variables) of the constraints are the rate by which the solution would improve if the constraints were relaxed.
  • Capacity Expansion is a foundational power systems decision problem.
  • We looked at a “greenfield” example: no existing plants.
  • Decision problem becomes more complex with renewables (HW4) or “brownfield” (expanding existing fleet, possibly with retirements).

Upcoming Schedule

Next Classes

Wednesday: Economic Dispatch

Next Week: Managing Air Pollution

Assessments

  • HW3 due Thursday (10/23)
  • Project Proposal due Friday (10/24)