Quick Start Guide
This quick start guide introduces the main concepts of using CapacityExpansion. For more detail on the different functionalities that CapacityExpansion provides, please refer to the subsequent chapters of the documentation or the examples in the examples folder.
Generally, the workflow consists of three steps:
- Data preparation of
ClustData
- Data preparation of
OptDataCEP
- Optimization
Example Workflow
After CapacityExpansion
and a Solver like, e.g. Clp
are installed, you can use them by saying:
julia> using CapacityExpansion
julia> using Clp
julia> optimizer=Clp.Optimizer # defines the optimizer used by CapacityExpansion
Clp.Optimizer
The first step is to load the time-series input data. The following example loads hourly wind, solar, and demand data for Germany (1 region) for the year 2016
. The hourly input-data is split into periods with 24 elements, which equals days.
julia> ts_input_data = load_timeseries_data_provided("GER_1"; T=24, years=[2016])
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│ caller = #add_timeseries_data!#12(::Int64, ::Int64, ::Array{Int64,1}, ::Function, ::Dict{String,Array}, ::SubString{String}, ::DataFrames.DataFrame) at load_data.jl:132
└ @ TimeSeriesClustering ~/.julia/packages/TimeSeriesClustering/3T6SG/src/utils/load_data.jl:132
ClustData("GER_1", [2016], 366, 24, Dict{String,Array}("solar-germany"=>[0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0],"wind-germany"=>[0.1429 0.1453 … 0.1329 0.1832; 0.1368 0.1758 … 0.1312 0.1802; … ; 0.1098 0.4955 … 0.1904 0.3122; 0.1254 0.4875 … 0.1865 0.3187],"demand_electricity-germany"=>[41913.0 39121.0 … 45343.0 45600.0; 40331.0 38271.0 … 44402.0 44332.0; … ; 44439.0 48859.0 … 50278.0 48988.0; 41257.0 45600.0 … 47534.0 47641.0]), [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 … 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0], Dict{String,Array}("solar-germany"=>[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 … 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],"wind-germany"=>[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 … 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],"demand_electricity-germany"=>[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 … 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]), Dict{String,Array}("solar-germany"=>[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 … 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],"wind-germany"=>[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 … 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],"demand_electricity-germany"=>[1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 … 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]), [1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0; … ; 1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10 … 357, 358, 359, 360, 361, 362, 363, 364, 365, 366])
The output ts_input_data
is a ClustData
data struct that contains the data and additional information about the data.
julia> ts_input_data.data # a dictionary with the data.
Dict{String,Array} with 3 entries:
"solar-germany" => [0.0 0.0 … 0.0 0.0; 0.0 0.0 … 0.0 0.0; … ; 0.…
"wind-germany" => [0.1429 0.1453 … 0.1329 0.1832; 0.1368 0.1758…
"demand_electricity-germany" => [41913.0 39121.0 … 45343.0 45600.0; 40331.0 3…
julia> ts_input_data.data["wind-germany"] # the wind data (choose solar, `demand_electricity` as other options in this example)
24×366 Array{Float64,2}:
0.1429 0.1453 0.4843 0.4279 0.248 … 0.3359 0.0793 0.1329 0.1832
0.1368 0.1758 0.4819 0.4186 0.2574 0.318 0.0803 0.1312 0.1802
0.1232 0.2135 0.4792 0.407 0.2682 0.2949 0.0791 0.1337 0.1779
0.1096 0.2466 0.4838 0.3976 0.2764 0.2739 0.0775 0.1363 0.1796
0.0964 0.2818 0.4917 0.3873 0.2784 0.2688 0.0774 0.1382 0.1872
0.082 0.3209 0.4862 0.3776 0.2797 … 0.2638 0.0781 0.1387 0.1971
0.0706 0.3548 0.4784 0.3655 0.2796 0.2419 0.08 0.1401 0.2072
0.0593 0.3921 0.471 0.3528 0.2828 0.2151 0.0818 0.1416 0.2151
0.0438 0.422 0.4786 0.3432 0.2878 0.1813 0.0777 0.1347 0.2067
0.0317 0.4536 0.475 0.3259 0.2823 0.1494 0.0653 0.1177 0.1981
⋮ ⋱ ⋮
0.0116 0.5288 0.4637 0.248 0.2738 … 0.1112 0.1266 0.2099 0.2792
0.0222 0.542 0.4832 0.262 0.2733 0.1005 0.1458 0.233 0.2855
0.0346 0.5416 0.4879 0.2673 0.2672 0.0811 0.1578 0.2339 0.2864
0.0497 0.5316 0.4804 0.2629 0.2585 0.0636 0.1626 0.2217 0.2926
0.0669 0.521 0.4651 0.2549 0.2511 0.0521 0.1627 0.2086 0.2974
0.0817 0.5103 0.4526 0.2448 0.2453 … 0.0472 0.1576 0.2014 0.2987
0.0948 0.5017 0.446 0.2379 0.2403 0.0517 0.1493 0.1961 0.3033
0.1098 0.4955 0.4387 0.2395 0.2356 0.0617 0.1431 0.1904 0.3122
0.1254 0.4875 0.4349 0.2433 0.2316 0.0725 0.1376 0.1865 0.3187
julia> ts_input_data.K # number of periods
366
The second step is to include the optimization data, which is not time-series depending.
julia> cep_data = load_cep_data_provided("GER_1")
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│ caller = load_cep_data_nodes(::String, ::OptVariable{OptDataCEPTech,1,Tuple{Array{Any,1}},Tuple{Dict{Any,Int64}}}) at load_data.jl:118
└ @ CapacityExpansion ~/build/YoungFaithful/CapacityExpansion.jl/src/utils/load_data.jl:118
┌ Warning: `getindex(df::DataFrame, col_ind::ColumnIndex)` is deprecated, use `df[!, col_ind]` instead.
│ caller = load_cep_data_costs(::String, ::OptVariable{OptDataCEPTech,1,Tuple{Array{Any,1}},Tuple{Dict{Any,Int64}}}, ::OptVariable{OptDataCEPNode,2,Tuple{Array{Any,1},Array{String,1}},Tuple{Dict{Any,Int64},Dict{String,Int64}}}) at load_data.jl:241
└ @ CapacityExpansion ~/build/YoungFaithful/CapacityExpansion.jl/src/utils/load_data.jl:241
OptDataCEP("GER_1", 5-dimensional OptVariable{Number,5,...} of type fv with index sets:
Dimension 1 - tech, Any["bat_in", "bat_out", "bat_e", "h2_in", "pv", "trans", "coal", "gas", "demand", "wind", "oil", "h2_e", "h2_out"]
Dimension 2 - node, ["germany"]
Dimension 3 - year, [2015]
Dimension 4 - account, ["cap_fix", "var"]
Dimension 5 - impact, ["EUR", "CO2"]
And data, a 13×1×1×2×2 Array{Number,5}:
[:, :, 2015, "cap_fix", "EUR"] =
8961.67479
0.0
25653.7085
479782.071
136926.376
51.0061474
153702.673
29964.5882
0.0
168417.541
38429.5834
68.4962783
0.0
[:, :, 2015, "var", "EUR"] =
0.01
0.0
0.0
1.9
0.0
0.0
10.708
86.564
0.0
0.0
153.586
0.0
0.0
[:, :, 2015, "cap_fix", "CO2"] =
0.0
0.0
1658.35472
96666.6667
160.233933
3.3275
13.8595556
1.2334
0.0
36.75
8.25
0.0
0.0
[:, :, 2015, "var", "CO2"] =
0.0
0.0
0.0
0.0
0.0
0.0
1217.49
728.71
0.0
0.0
874.6
0.0
0.0 , 1-dimensional OptVariable{OptDataCEPTech,1,...} of type fv with index sets:
Dimension 1 - tech, Any["bat_in", "bat_out", "bat_e", "h2_in", "pv", "trans", "coal", "gas", "demand", "wind", "oil", "h2_e", "h2_out"]
And data, a 13-element Array{OptDataCEPTech,1}:
OptDataCEPTech("Battery Charge", ["conversion", "all"], "power", "node", 25, 30, 0.04, 0.0640119628, Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}("carrier"=>"electricity_bat"), Dict{Any,Any}("efficiency"=>0.97))
OptDataCEPTech("Battery Discharge", ["conversion", "all"], "power", "node", 25, 30, 0.04, 0.0640119628, Dict{Any,Any}("carrier"=>"electricity_bat"), Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}("efficiency"=>0.97,"cap_eq"=>"bat_in"))
OptDataCEPTech("Battery Storage", ["storage", "all"], "energy", "node", 25, 30, 0.04, 0.0640119628, Dict{Any,Any}("carrier"=>"electricity_bat"), Dict{Any,Any}("carrier"=>"electricity_bat"), Dict{Any,Any}("efficiency"=>0.93))
OptDataCEPTech("Hydrogen Storage Charge", ["conversion", "all"], "power", "node", 15, 30, 0.04, 0.0899411004, Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}("carrier"=>"hydrogen"), Dict{Any,Any}("efficiency"=>0.83))
OptDataCEPTech("Photo Voltaic", ["non_dispatchable_generation", "generation", "all"], "power", "node", 15, 30, 0.04, 0.0899411004, Dict{Any,Any}("timeseries"=>"solar"), Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}())
OptDataCEPTech("Transmission Line", ["transmission", "all"], "power", "line", 80, 30, 0.04, 0.0578300991, Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}("efficiency"=>0.9995))
OptDataCEPTech("Coal Plant", ["dispatchable_generation", "generation", "all"], "power", "node", 45, 30, 0.04, 0.0578300991, Dict{Any,Any}("fuel"=>"coal"), Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}())
OptDataCEPTech("Gas Plant", ["dispatchable_generation", "generation", "all"], "power", "node", 50, 30, 0.04, 0.0578300991, Dict{Any,Any}("fuel"=>"gas"), Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}())
OptDataCEPTech("Electricity demand", ["demand", "all"], "power", "node", 15, 30, 0.04, 0.0899411004, Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}("timeseries"=>"demand_electricity"), Dict{Any,Any}())
OptDataCEPTech("Onshore Wind", ["non_dispatchable_generation", "generation", "all"], "power", "node", 15, 30, 0.04, 0.0899411004, Dict{Any,Any}("timeseries"=>"wind"), Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}())
OptDataCEPTech("Oil Plant", ["dispatchable_generation", "generation", "all"], "power", "node", 40, 30, 0.04, 0.0578300991, Dict{Any,Any}("fuel"=>"oil"), Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}())
OptDataCEPTech("Hydrogen Storage", ["storage", "all"], "energy", "node", 25, 30, 0.04, 0.0640119628, Dict{Any,Any}("carrier"=>"hydrogen"), Dict{Any,Any}("carrier"=>"hydrogen"), Dict{Any,Any}("efficiency"=>0.99))
OptDataCEPTech("Hydrogen Storage Discharge", ["conversion", "all"], "power", "node", 40, 30, 0.04, 0.0578300991, Dict{Any,Any}("carrier"=>"hydrogen"), Dict{Any,Any}("carrier"=>"electricity"), Dict{Any,Any}("efficiency"=>0.53,"cap_eq"=>"h2_in")), 2-dimensional OptVariable{OptDataCEPNode,2,...} of type fv with index sets:
Dimension 1 - tech, Any["bat_in", "bat_out", "bat_e", "h2_in", "pv", "trans", "coal", "gas", "demand", "wind", "oil", "h2_e", "h2_out"]
Dimension 2 - node, ["germany"]
And data, a 13×1 Array{OptDataCEPNode,2}:
OptDataCEPNode("germany", 0, 1000000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 0, 1000000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 0, 1000000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 0, 1000000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 32312, 1000000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 0, 0, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 45027, 100000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 22370, 100000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 1, 1, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 31827, 1000000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 7004, 100000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 0, 10000000000, "GER", LatLon(lat=51.167261°, lon=10.450738°))
OptDataCEPNode("germany", 0, 1000000, "GER", LatLon(lat=51.167261°, lon=10.450738°)) , 2-dimensional OptVariable{OptDataCEPLine,2,...} of type fv with index sets:
Dimension 1 - tech, String[]
Dimension 2 - line, String[]
And data, a 0×0 Array{OptDataCEPLine,2})
The cep
is a OptDataCEP
data struct.
julia> cep_data.region # the region of the input-data
"GER_1"
julia> cep_data.costs # the information of costs as an `OptVariable` with 5 dimensions
5-dimensional OptVariable{Number,5,...} of type fv with index sets:
Dimension 1 - tech, Any["bat_in", "bat_out", "bat_e", "h2_in", "pv", "trans", "coal", "gas", "demand", "wind", "oil", "h2_e", "h2_out"]
Dimension 2 - node, ["germany"]
Dimension 3 - year, [2015]
Dimension 4 - account, ["cap_fix", "var"]
Dimension 5 - impact, ["EUR", "CO2"]
And data, a 13×1×1×2×2 Array{Number,5}:
[:, :, 2015, "cap_fix", "EUR"] =
8961.67479
0.0
25653.7085
479782.071
136926.376
51.0061474
153702.673
29964.5882
0.0
168417.541
38429.5834
68.4962783
0.0
[:, :, 2015, "var", "EUR"] =
0.01
0.0
0.0
1.9
0.0
0.0
10.708
86.564
0.0
0.0
153.586
0.0
0.0
[:, :, 2015, "cap_fix", "CO2"] =
0.0
0.0
1658.35472
96666.6667
160.233933
3.3275
13.8595556
1.2334
0.0
36.75
8.25
0.0
0.0
[:, :, 2015, "var", "CO2"] =
0.0
0.0
0.0
0.0
0.0
0.0
1217.49
728.71
0.0
0.0
874.6
0.0
0.0
The third step is to setup the model and run the optimization.
julia> result = run_opt(ts_input_data,cep_data,optimizer;optimizer_config=Dict{Symbol,Any}(:LogLevel => 0));
┌ Warning: Limit is reached for techs ["demand-germany"]
└ @ CapacityExpansion ~/build/YoungFaithful/CapacityExpansion.jl/src/utils/utils.jl:249
[ Info: Solved Scenario : OPTIMAL min COST: 1.671e10 [EUR] ⇨ 33.03 [EUR per MWh] s.t.
The result
is a OptResult
data struct and contains the information of the optimization result.
julia> result.info["model"] # the equations of the setup model
19-element Array{String,1}:
""
"Variable COST[account, impact, tech] in EUR CO2 "
"Variable CAP[tech_n, infrastruct, nodes] ≥ 0 in MW"
"Variable GEN[tech_power, carrier, t, k, node] in MW"
"COST['var',impact,tech] = (1) ⋅ Σ_{t,k,node}GEN[tech, carrier_input, t, k, node]⋅ ts_weights[k] ⋅ ts_deltas[t,k]⋅ var_costs[tech,impact] ∀ impact, tech_demand"
"COST['cap_fix',impact,tech] = Σ_{t,k}(ts_weights ⋅ ts_deltas[t,k])/8760h ⋅ Σ_{node}CAP[tech,'new',node] ⋅ cap_costs[tech,impact] ∀ impact, tech_demand"
"GEN[tech, carrier, t, k, node] = (-1) ⋅ Σ_{infrastruct} CAP[tech,infrastruct,node] * ts[tech-node,t,k] ∀ node, tech_demand, t, k"
"COST['var',impact,tech] = (1) ⋅ Σ_{t,k,node}GEN[tech, carrier_output, t, k, node]⋅ ts_weights[k] ⋅ ts_deltas[t,k]⋅ var_costs[tech,impact] ∀ impact, tech_non_dispatchable_generation"
"COST['cap_fix',impact,tech] = Σ_{t,k}(ts_weights ⋅ ts_deltas[t,k])/8760h ⋅ Σ_{node}CAP[tech,'new',node] ⋅ cap_costs[tech,impact] ∀ impact, tech_non_dispatchable_generation"
"0 ≤ GEN[tech, carrier, t, k, node] ≤ Σ_{infrastruct}CAP[tech,infrastruct,node]*ts[tech-node,t,k] ∀ node, tech_generation{non_dispatchable}, t, k"
"COST['var',impact,tech] = (1) ⋅ Σ_{t,k,node}GEN[tech, carrier_output, t, k, node]⋅ ts_weights[k] ⋅ ts_deltas[t,k]⋅ var_costs[tech,impact] ∀ impact, tech_dispatchable_generation"
"COST['cap_fix',impact,tech] = Σ_{t,k}(ts_weights ⋅ ts_deltas[t,k])/8760h ⋅ Σ_{node}CAP[tech,'new',node] ⋅ cap_costs[tech,impact] ∀ impact, tech_dispatchable_generation"
"0 ≤ GEN[tech, carrier, t, k, node] ≤ Σ_{infrastruct} CAP[tech,infrastruct,node] ∀ node, tech_dispatchable_generation, t, k"
"CAP[tech, 'ex', node] = existing infrastructure ∀ node, tech ∈ tech_group_ex"
"CAP[tech, 'ex', node] = 0 ∀ node, tech ∉ tech_group_ex"
"∑_{infrastuct} CAP[tech, infrastruct, node] <= limit infrastructure ∀ tech_n, node"
"Σ_{tech,node}GEN[tech, carrier, t,k,node] + SLACK[carrier,t,k,node] = 0 ∀ t,k"
"Σ_{tech,node}GEN[tech, carrier, t,k,node] = 0 ∀ t,k"
"min Σ_{account,tech}COST[account,'EUR',tech] + Σ_{node,carrier_ll} LL[carrier,node] ⋅ lost_load_cost[carrier]) + Σ_{impact_le} LE[impact] ⋅ lost_emission_cost[impact] st. above"
julia> result.status # the status of the optimization
:OPTIMAL
julia> result.objective # the value of the objective
1.6708870355646713e10
julia> result.variables["CAP"] # the newly installed and existing capacities of the different technologies along the nodes. Other options are "COST" (the costs) and "GEN" (the generation)
3-dimensional OptVariable{Float64,3,...} of type dv with index sets:
Dimension 1 - tech, ["pv", "coal", "gas", "demand", "wind", "oil"]
Dimension 2 - infrastruct, ["new", "ex"]
Dimension 3 - nodes, ["germany"]
And data, a 6×2×1 Array{Float64,3}:
[:, :, "germany"] =
0.0 0.0
68521.0 0.0
10966.0 0.0
0.0 1.0
0.0 0.0
0.0 0.0
julia> result.sets["tech"]["generation"] # a `"tech"` (dimension) set of all `"generation"` (tech-group) within the model
5-element Array{String,1}:
"pv"
"coal"
"gas"
"wind"
"oil"
julia> result.config["generation"] # Detailed model configuration
true