Capacity cost link

This example illustrates the usage of CapacityCostLink from EnergyModelsFlex. The example consists of a cheap source, an expensive source, and a sink. Two links connect these nodes: a Direct link from the expensive source and a CapacityCostLink from the cheap source. The model compares the operational costs and capacity utilization of these two routing options across three strategic periods with varying capacity costs. There is a peak demand in the first two operational periods (at 10 and 9 MW) that must be covered followed by low demand (1 MW) for the remaining operational periods. Two sub periods are defined for the CapacityCostLink, allowing it to optimize its capacity usage based on the varying capacity costs over the year.

Start by importing the required packages

using TimeStruct
using EnergyModelsBase
using EnergyModelsFlex

using HiGHS
using JuMP
using PrettyTables

const EMF = EnergyModelsFlex
EnergyModelsFlex

Define the different resources

power = ResourceCarrier("Power", 0.0)
co2 = ResourceEmit("CO₂", 0.0)
𝒫 = [power, co2]
2-element Vector{Resource}:
 Power
 CO₂

Creation of the time structure and global data

op_number = 24
𝒯 = TwoLevel([1, 2, 10], SimpleTimes(op_number, 1); op_per_strat = 8760)
modeltype = OperationalModel(
    Dict(co2 => FixedProfile(10)),
    Dict(co2 => FixedProfile(0)),
    co2,
)
OperationalModel(Dict{ResourceEmit{Float64}, FixedProfile{Int64}}(CO₂ => FixedProfile{Int64}(10)), Dict{ResourceEmit{Float64}, FixedProfile{Int64}}(CO₂ => FixedProfile{Int64}(0)), CO₂)

Create the nodes

src_cheap = RefSource(
    "cheap source",
    FixedProfile(10),
    FixedProfile(100),
    FixedProfile(0),
    Dict(power => 1),
)
src_exp = RefSource(
    "expensive source",
    FixedProfile(10),
    FixedProfile(400),
    FixedProfile(0),
    Dict(power => 1),
)
sink = RefSink(
    "sink",
    OperationalProfile([10, 9, fill(1, op_number-2)...]),
    Dict(:surplus => FixedProfile(4), :deficit => FixedProfile(1e4)),
    Dict(power => 1),
)
n_sink

Collect the nodes

𝒩 = [src_cheap, src_exp, sink]
3-element Vector{EnergyModelsBase.Node}:
 n_cheap source
 n_expensive source
 n_sink

Connect the nodes

l_direct = Direct("Direct link", src_exp, sink, Linear())
l_capacity = CapacityCostLink(
    "Capacity cost link",
    src_cheap,                          # from
    sink,                               # to
    FixedProfile(10),                   # capacity
    StrategicProfile([5e5, 1e6, 2e6]),  # capacity price
    2,                                  # capacity price period
    power,                              # capacity constrained resource
)
ℒ = [l_direct, l_capacity]
2-element Vector{Link}:
 l_n_expensive source-n_sink
 l_n_cheap source-n_sink

Input data structure and modeltype creation

case = Case(𝒯, 𝒫, [𝒩, ℒ])
Case(TwoLevel{Int64, Int64, SimpleTimes{Int64}}(3, [1, 2, 10], SimpleTimes{Int64}[SimpleTimes{Int64}(24, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1  …  1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), SimpleTimes{Int64}(24, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1  …  1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), SimpleTimes{Int64}(24, [1, 1, 1, 1, 1, 1, 1, 1, 1, 1  …  1, 1, 1, 1, 1, 1, 1, 1, 1, 1])], 8760.0), Resource[Power, CO₂], Vector[EnergyModelsBase.Node[n_cheap source, n_expensive source, n_sink], Link[l_n_expensive source-n_sink, l_n_cheap source-n_sink]], Vector{Function}[[EnergyModelsBase.get_nodes, EnergyModelsBase.get_links]], Dict{Any, Any}())

Create and optimize the model

optimizer = optimizer_with_attributes(HiGHS.Optimizer, MOI.Silent() => true)
m = create_model(case, modeltype)
set_optimizer(m, optimizer)
optimize!(m)

Extract data

demand = value.(m[:cap_use][sink, :])
direct_flow = [value(m[:link_in][l_direct, t, power]) for t ∈ 𝒯]
capacitycostlink_flow = [value(m[:link_in][l_capacity, t, power]) for t ∈ 𝒯]
periods = collect(𝒯)

opex_var_cheap = value.(m[:opex_var][src_cheap, :])
opex_var_expensive = value.(m[:opex_var][src_exp, :])
link_opex_var = value.(m[:link_opex_var][l_capacity, :])

𝒯ⁱⁿᵛ = collect(strategic_periods(𝒯))
cap_price = [EMF.cap_price(l_capacity)[t] for t ∈ 𝒯ⁱⁿᵛ]
3-element Vector{Float64}:
 500000.0
      1.0e6
      2.0e6

From the table below we see that the Direct link is used more when the CapacityCostLink has a high capacity price, e.g., in strategic periods

  1. In contrast, when the capacity price is low, e.g., in periods

1 and 2, the CapacityCostLink is used, but not more than 1 MW as any higher amount would result in higher max_cap_use_sub_period cost just to be able to cover the two first operational periods

pretty_table(
    hcat(periods, demand, direct_flow, capacitycostlink_flow);
    column_labels                     = [
    ["Period", "Demand", "Flow", "Flow"],
    ["", "Sink", "Direct", "CapacityCostLink"]],
    fit_table_in_display_horizontally = false,
    fit_table_in_display_vertically   = false,
    maximum_number_of_rows            = -1,
    maximum_number_of_columns         = -1,
)
┌─────────┬────────┬────────┬──────────────────┐
│  Period  Demand    Flow              Flow │
│            Sink  Direct  CapacityCostLink │
├─────────┼────────┼────────┼──────────────────┤
│  sp1-t1 │   10.0 │    9.0 │              1.0 │
│  sp1-t2 │    9.0 │    8.0 │              1.0 │
│  sp1-t3 │    1.0 │   -0.0 │              1.0 │
│  sp1-t4 │    1.0 │   -0.0 │              1.0 │
│  sp1-t5 │    1.0 │   -0.0 │              1.0 │
│  sp1-t6 │    1.0 │   -0.0 │              1.0 │
│  sp1-t7 │    1.0 │   -0.0 │              1.0 │
│  sp1-t8 │    1.0 │   -0.0 │              1.0 │
│  sp1-t9 │    1.0 │   -0.0 │              1.0 │
│ sp1-t10 │    1.0 │   -0.0 │              1.0 │
│ sp1-t11 │    1.0 │   -0.0 │              1.0 │
│ sp1-t12 │    1.0 │   -0.0 │              1.0 │
│ sp1-t13 │    1.0 │   -0.0 │              1.0 │
│ sp1-t14 │    1.0 │   -0.0 │              1.0 │
│ sp1-t15 │    1.0 │   -0.0 │              1.0 │
│ sp1-t16 │    1.0 │   -0.0 │              1.0 │
│ sp1-t17 │    1.0 │   -0.0 │              1.0 │
│ sp1-t18 │    1.0 │   -0.0 │              1.0 │
│ sp1-t19 │    1.0 │   -0.0 │              1.0 │
│ sp1-t20 │    1.0 │   -0.0 │              1.0 │
│ sp1-t21 │    1.0 │   -0.0 │              1.0 │
│ sp1-t22 │    1.0 │   -0.0 │              1.0 │
│ sp1-t23 │    1.0 │   -0.0 │              1.0 │
│ sp1-t24 │    1.0 │   -0.0 │              1.0 │
│  sp2-t1 │   10.0 │    9.0 │              1.0 │
│  sp2-t2 │    9.0 │    8.0 │              1.0 │
│  sp2-t3 │    1.0 │   -0.0 │              1.0 │
│  sp2-t4 │    1.0 │   -0.0 │              1.0 │
│  sp2-t5 │    1.0 │   -0.0 │              1.0 │
│  sp2-t6 │    1.0 │   -0.0 │              1.0 │
│  sp2-t7 │    1.0 │   -0.0 │              1.0 │
│  sp2-t8 │    1.0 │   -0.0 │              1.0 │
│  sp2-t9 │    1.0 │   -0.0 │              1.0 │
│ sp2-t10 │    1.0 │   -0.0 │              1.0 │
│ sp2-t11 │    1.0 │   -0.0 │              1.0 │
│ sp2-t12 │    1.0 │   -0.0 │              1.0 │
│ sp2-t13 │    1.0 │   -0.0 │              1.0 │
│ sp2-t14 │    1.0 │   -0.0 │              1.0 │
│ sp2-t15 │    1.0 │   -0.0 │              1.0 │
│ sp2-t16 │    1.0 │   -0.0 │              1.0 │
│ sp2-t17 │    1.0 │   -0.0 │              1.0 │
│ sp2-t18 │    1.0 │   -0.0 │              1.0 │
│ sp2-t19 │    1.0 │   -0.0 │              1.0 │
│ sp2-t20 │    1.0 │   -0.0 │              1.0 │
│ sp2-t21 │    1.0 │   -0.0 │              1.0 │
│ sp2-t22 │    1.0 │   -0.0 │              1.0 │
│ sp2-t23 │    1.0 │   -0.0 │              1.0 │
│ sp2-t24 │    1.0 │   -0.0 │              1.0 │
│  sp3-t1 │   10.0 │   10.0 │             -0.0 │
│  sp3-t2 │    9.0 │    9.0 │             -0.0 │
│  sp3-t3 │    1.0 │    1.0 │             -0.0 │
│  sp3-t4 │    1.0 │    1.0 │             -0.0 │
│  sp3-t5 │    1.0 │    1.0 │             -0.0 │
│  sp3-t6 │    1.0 │    1.0 │             -0.0 │
│  sp3-t7 │    1.0 │    1.0 │             -0.0 │
│  sp3-t8 │    1.0 │    1.0 │             -0.0 │
│  sp3-t9 │    1.0 │    1.0 │             -0.0 │
│ sp3-t10 │    1.0 │    1.0 │             -0.0 │
│ sp3-t11 │    1.0 │    1.0 │             -0.0 │
│ sp3-t12 │    1.0 │    1.0 │             -0.0 │
│ sp3-t13 │    1.0 │    1.0 │             -0.0 │
│ sp3-t14 │    1.0 │    1.0 │             -0.0 │
│ sp3-t15 │    1.0 │    1.0 │             -0.0 │
│ sp3-t16 │    1.0 │    1.0 │             -0.0 │
│ sp3-t17 │    1.0 │    1.0 │             -0.0 │
│ sp3-t18 │    1.0 │    1.0 │             -0.0 │
│ sp3-t19 │    1.0 │    1.0 │             -0.0 │
│ sp3-t20 │    1.0 │    1.0 │             -0.0 │
│ sp3-t21 │    1.0 │    1.0 │             -0.0 │
│ sp3-t22 │    1.0 │    1.0 │             -0.0 │
│ sp3-t23 │    1.0 │    1.0 │             -0.0 │
│ sp3-t24 │    1.0 │    1.0 │             -0.0 │
└─────────┴────────┴────────┴──────────────────┘

Display operational expenditures

From the table below we see that the CapacityCostLink is used (has OPEX) only when the capacity price is sufficiently low (strategic periods 1 and 2). When the capacity price is high (strategic period 3), the CapacityCostLink is not used, and the Direct link (from the expensive source) covers the demand instead.

pretty_table(
    hcat(𝒯ⁱⁿᵛ, cap_price, link_opex_var, opex_var_cheap, opex_var_expensive);
    column_labels                     = [
    ["Period", "Capacity Price", "OPEX (link)", "OPEX (cheap node)", "OPEX (expensive node)"],
    ["", "CapacityCostLink", "CapacityCostLink", "RefSource", "RefSource"]],
    formatters                        = [fmt__printf("%5.3g")],
    fit_table_in_display_horizontally = false,
    fit_table_in_display_vertically   = false,
    maximum_number_of_rows            = -1,
    maximum_number_of_columns         = -1,
)
┌────────┬──────────────────┬──────────────────┬───────────────────┬───────────────────────┐
│ Period    Capacity Price       OPEX (link)  OPEX (cheap node)  OPEX (expensive node) │
│         CapacityCostLink  CapacityCostLink          RefSource              RefSource │
├────────┼──────────────────┼──────────────────┼───────────────────┼───────────────────────┤
│    sp1 │            5e+05 │            1e+06 │          8.76e+05 │              2.48e+06 │
│    sp2 │            1e+06 │            2e+06 │          8.76e+05 │              2.48e+06 │
│    sp3 │            2e+06 │               -0 │                -0 │              5.99e+06 │
└────────┴──────────────────┴──────────────────┴───────────────────┴───────────────────────┘

This page was generated using Literate.jl.