Capacity Expansion Problem

Capacity Expansion Problem

General

The capacity expansion problem (CEP) is designed as a linear optimization model. It is implemented in the algebraic modeling language JUMP. The implementation within JuMP allows to optimize multiple models in parallel and handle the steps from data input to result analysis and diagram export in one open source programming language. The coding of the model enables scalability based on the provided data input, single command based configuration of the setup model, result and configuration collection for further analysis and the opportunity to run design and operation in different optimizations.

Plot

The basic idea for the energy system is to have a spacial resolution of the energy system in discrete nodes. Each node has demand, non-dispatchable generation, dispatachable generation and storage capacities of varying technologies connected to itself. The different energy system nodes are interconnected with each other by transmission lines. The model is designed to minimize social costs by minimizing the following objective function:

\[min \sum_{account,tech}COST_{account,'EUR/USD',tech} + \sum LL \cdot cost_{LL} + LE \cdot cos_{LE}\]

Variables and Sets

The models scalability is relying on the usage of sets. The elements of the sets are extracted from the input data and scale the different variables. An overview of the sets is provided in the table. Depending on the models configuration the necessary sets are initialized.

namedescription
linestransmission lines connecting the nodes
nodesspacial energy system nodes
techfossil and renewable generation as well as storage technologies
impactimpact categories like EUR or USD, CO 2 − eq., ...
accountfixed costs for installation and yearly expenses, variable costs
infrastructinfrastructure status being either new or existing
sectorenergy sector like electricity
time Knumeration of the representative periods
time Tnumeration of the time intervals within a period
time T enumeration of the time steps within a period
time Inumeration of the time invervals of the full input data periods
time I enumeration of the time steps of the full input data periods
dir transmissiondirection of the flow uniform with or opposite to the lines direction

An overview of the variables used in the CEP is provided in the table:

namedimensionsunitdescription
COST[account,impact,tech]EUR/USD, LCA-categoriesCosts
CAP[tech,infrastruct,node]MWCapacity
GEN[sector,tech,t,k,node]MWGeneration
SLACK[sector,t,k,node]MWPower gap, not provided by installed CAP
LL[sector]MWhLoastLoad Generation gap, not provided by installed CAP
LE[impact]LCA-categoriesLoastEmission Amount of emissions that installed CAP crosses the Emission constraint
INTRASTOR[sector, tech,t,k,node]MWhStorage level within a period
INTERSTOR[sector,tech,i,node]MWhStorage level between periods of the full time series
FLOW[sector,dir,tech,t,k,line]MWFlow over transmission line
TRANS[tech,infrastruct,lines]MWmaximum capacity of transmission lines

Data

The package provides data Capacity Expansion Data for:

namenodeslinesyearstech
GER_11 – germany as single nodenone2006-2016Pv, wind, coal, oil, gas, bate, batin, batout, h2e, h2in, h2out, trans
GER_1818 – dena-zones within germany492015Pv, wind, coal, oil, gas, bate, batin, batout, h2e, h2in, h2out, trans
CA_11 - california as single nodenone2016Pv, wind, coal, oil, gas, bate, batin, batout, h2e, h2in, h2out, trans
CA_14 ! currently not included !14 – multiple nodes within CA and neighboring states462016Pv, wind, coal, oil, gas, bate, batin, batout, h2e, h2in, h2out, trans
TX_11 – single node within Texasnone2008Pv, wind, coal, nuc, gas, bate, batin, bat_out

Opt Types

 OptDataCEP <: OptData

-region::String name of state or region data belongs to -nodes::DataFrame nodes x region, infrastruct, capacityofdifferenttech... -`varcosts::DataFrametech x [USD, CO2] -fixcosts::DataFrametech x [USD, CO2] -capcosts::DataFrametech x [USD, CO2] -techs::DataFrame` tech x [categ,sector,lifetime,effic,fuel,annuityfactor] instead of USD you can also use your favorite currency like EUR

source

OptResult

source
 OptVariable

-data::Array - includes the optimization variable output in form of an array -axes_names::Array{String,1}- includes the names of the different axes and is equivalent to the sets in the optimization formulation -axes::Tuple- includes the values of the different axes of the optimization variables -type::String` - defines the type of the variable being cv - cost variable - dv -design variable - ov - operating variable - sv - slack variable

source
 Scenario

-descriptor::String -clust_res::ClustResult -opt_res::OptResult

source

Running the Capacity Expansion Problem

Note

The CEP model can be run with many configurations. The configurations themselves don't mess with each other though the provided input data must fulfill the ability to have e.g. lines in order for transmission to work.

An overview is provided in the following table:

descriptionunitconfigurationvaluestypedefault value
enforce a CO2-limitkg-CO2-eq./MWco2_limit>0::NumberInf
including existing infrastructure (no extra costs)-existing_infrastructuretrue or false::Boolfalse
type of storage implementation-storage"none", "simple" or "seasonal"::String"none"
allowing transmission-transmissiontrue or false::BoolFALSE
fixing design variables and turning capacity expansion problem into dispatch problem-fixeddesignvariablesdesign variables from design run or nothing::OptVariablesnothing
allowing lost load (just necessary if design variables fixed)price/MWhlostelload_cost>1e6::NumberInf
allowing lost emission (just necessary if design variables fixed)price/kg_CO2-eq.lostCO2emission_cost>700::NumberInf

They can be applied in the following way:

ClustForOpt.run_optFunction.
run_opt(ts_data::ClustData,opt_data::OptDataCEP,opt_config::Dict{String,Any};solver::Any=CbcSolver())

organizing the actual setup and run of the CEP-Problem

source
 run_opt(ts_data::ClustData,opt_data::OptDataCEP,fixed_design_variables::Dict{String,OptVariable};solver::Any=CbcSolver(),lost_el_load_cost::Number=Inf,lost_CO2_emission_cost::Number)

Wrapper function for type of optimization problem for the CEP-Problem (NOTE: identifier is the type of opt_data - in this case OptDataCEP - so identification as CEP problem) This problem runs the operational optimization problem only, with fixed design variables. provide the fixed design variables and the opt_config of the previous step (design run or another opterational run) what you can add to the opt_config:

  • lost_el_load_cost: Number indicating the lost load price/MWh (should be greater than 1e6), give Inf for none
  • lost_CO2_emission_cost: Number indicating the emission price/kg-CO2 (should be greater than 1e6), give Inf for none
  • give Inf for both lost_cost for no slack
source
 run_opt(ts_data::ClustData,opt_data::OptDataCEP,fixed_design_variables::Dict{String,OptVariable};solver::Any=CbcSolver(),descriptor::String="",co2_limit::Number=Inf,lost_el_load_cost::Number=Inf,lost_CO2_emission_cost::Number=Inf,existing_infrastructure::Bool=false,intrastorage::Bool=false)

Wrapper function for type of optimization problem for the CEP-Problem (NOTE: identifier is the type of opt_data - in this case OptDataCEP - so identification as CEP problem) options to tweak the model are:

  • descritor: String with the name of this paricular model like "kmeans-10-co2-500"
  • co2_limit: A number limiting the kg.-CO2-eq./MWh (normally in a range from 5-1250 kg-CO2-eq/MWh), give Inf or no kw if unlimited
  • lost_el_load_cost: Number indicating the lost load price/MWh (should be greater than 1e6), give Inf for none
  • lost_CO2_emission_cost:
    • Number indicating the emission price/kg-CO2 (should be greater than 1e6), give Inf for none
    • give Inf for both lost_cost for no slack
  • existing_infrastructure: true or false to include or exclude existing infrastructure to the model
  • storage: String "none" for no storage or "simple" to include simple (only intra-day storage) or "seasonal" to include seasonal storage (inter-day)
source

Examples

Example with CO2-Limitation

using ClustForOpt
state="GER_1" #select state
ts_input_data, = load_timeseries_data("CEP", state; K=365, T=24)
cep_data = load_cep_data(state)
ts_clust_data = run_clust(ts_input_data;method="kmeans",representation="centroid",n_init=5,n_clust=5).best_results
solver=CbcSolver() # select solver
# tweak the CO2 level
co2_result = run_opt(ts_clust_data,cep_data;solver=solver,descriptor="co2",co2_limit=500)
co2_result.status

Example with slack variables included

slack_result = run_opt(ts_clust_data,cep_data;solver=solver,descriptor="slack",lost_el_load_cost=1e6, lost_CO2_emission_cost=700)

Example for simple storage

Note

In simple or intradaystorage the storage level is enforced to be the same at the beginning and end of each day. The variable 'INTRASTORAGE' is tracking the storage level within each day of the representative periods.

simplestor_result = run_opt(ts_clust_data,cep_data;solver=solver,descriptor="simple storage",storage="simple")

Example for seasonal storage

Note

In seasonalstorage the storage level is enforced to be the same at the beginning and end of the original time-series. The new variable 'INTERSTORAGE' tracks the storage level throughout the days (or periods) of the original time-series. The variable 'INTRASTORAGE' is tracking the storage level within each day of the representative periods.

seasonalstor_result = run_opt(ts_clust_data,cep_data;solver=solver,descriptor="seasonal storage",storage="seasonal",k_ids=run_clust(ts_input_data;method="kmeans",representation="centroid",n_init=5,n_clust=5).best_ids)

Get Functions

The get functions allow an easy access to the information included in the result.

get_cep_variable_set(variable::OptVariable,num_index_set::Int)

Get the variable set from the specific variable and the num_index_set like 1

source
get_cep_variable_set(scenario::Scenario,var_name::String,num_index_set::Int)

Get the variable set from the specific Scenario by indicating the var_name e.g. "COST" and the num_index_set like 1

source
get_cep_variable_value(variable::OptVariable,index_set::Array)

Get the variable data from the specific Scenario by indicating the var_name e.g. "COST" and the index_set like [:;"EUR";"pv"]

source
get_cep_variable_value(scenario::Scenario,var_name::String,index_set::Array)

Get the variable data from the specific Scenario by indicating the var_name e.g. "COST" and the index_set like [:;"EUR";"pv"]

source
get_cep_slack_variables(opt_result::OptResult)

Returns all slack variables in this opt_result mathing the type "sv"

source
get_cep_design_variables(opt_result::OptResult)

Returns all design variables in this opt_result mathing the type "dv"

source

Examples

Example plotting Capacities

co2_result = run_opt(ts_clust_data,cep_data;solver=solver,descriptor="co2",co2_limit=500) #hide
using Plots
# use the get variable set in order to get the labels: indicate the variable as "CAP" and the set-number as 1 to receive those set values
variable=co2_result.variables["CAP"]
labels=get_cep_variable_set(variable,1)
# use the get variable value function to recieve the values of CAP[:,:,1]
data=get_cep_variable_value(variable,[:,:,1])
# use the data provided for a simple bar-plot without a legend
bar(data,title="Cap", xticks=(1:length(labels),labels),legend=false)