Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optconfig #37

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
20 changes: 10 additions & 10 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ makedocs(sitename="CapacityExpansion.jl",
"Teaching" => ["teaching.md", "homework.md"],
],
format = Documenter.HTML(assets = [
"assets/clust_for_opt_text.svg",
"assets/opt_cep.svg",
"assets/workflow.svg",
"assets/GER_1.svg",
"assets/GER_18.svg",
"assets/CA_1.svg",
"assets/CA_14.svg",
"assets/opt_cep_cap_plot",
"assets/preparing_clust_data_load",
"assets/preparing_clust_data_agg"])
asset("assets/cep_text.svg", class=:ico, islocal=true),
asset("assets/opt_cep.svg", class=:ico, islocal=true),
asset("assets/workflow.svg", class=:ico, islocal=true),
asset("assets/GER_1.svg", class=:ico, islocal=true),
asset("assets/GER_18.svg", class=:ico, islocal=true),
asset("assets/CA_1.svg", class=:ico, islocal=true),
asset("assets/CA_14.svg", class=:ico, islocal=true),
asset("assets/opt_cep_cap_plot.svg", class=:ico, islocal=true),
asset("assets/preparing_clust_data_load.svg", class=:ico, islocal=true),
asset("assets/preparing_clust_data_agg.svg", class=:ico, islocal=true)])
)

deploydocs(repo = "github.com/YoungFaithful/CapacityExpansion.jl.git", devbranch="dev")
39 changes: 21 additions & 18 deletions examples/workflow_example_cep.jl
Original file line number Diff line number Diff line change
Expand Up @@ -7,54 +7,57 @@ years=[2016] #2016 works for GER_1 and CA_1, GER_1 can also be used with 2006 to
# laod ts-data
ts_input_data = load_timeseries_data_provided(state;T=24, years=years) #CEP
# load cep-data
cep_data = load_cep_data_provided(state)
opt_data = load_cep_data_provided(state)
## CLUSTERING ##
# run aggregation with kmeans
ts_clust_data = run_clust(ts_input_data;method="hierarchical",representation="centroid",n_init=1,n_clust=5) # default k-means make sure that n_init is high enough otherwise the results could be crap and drive you crazy

# run aggregation with kmeans and have periods segmented
ts_seg_data = run_clust(ts_input_data;method="kmeans",representation="centroid",n_init=100,n_clust=5, n_seg=4) # default k-means make sure that n_init is high enough otherwise the results could be crap and drive you crazy
# extract clust_data
clust_data = ts_clust_data.clust_data

## OPTIMIZATION EXAMPLES##
# select optimizer
optimizer=Clp.Optimizer

# tweak the CO2 level
co2_result = run_opt(ts_clust_data.clust_data,cep_data,optimizer;limit_emission=Dict{String,Number}("CO2/electricity"=>50)) #Within the example we limit the emitted carbon dioxide (in kg-CO2e) per electric energy consumed (in MWh). Generally values between 1250 kg-CO2e/MWh and 10 kg-CO2e/MWh are interesting

co2_opt_config = OptConfig(clust_data,opt_data,optimizer;limit_emission=Dict{String,Number}("CO2/electricity"=>50)) #Within the example we limit the emitted carbon dioxide (in kg-CO2e) per electric energy consumed (in MWh). Generally values between 1250 kg-CO2e/MWh and 10 kg-CO2e/MWh are interesting
# run the optimization
co2_result = run_opt(clust_data,opt_data,co2_opt_config)
# Include a Slack-Variable
slack_result = run_opt(ts_clust_data.clust_data,cep_data,optimizer;lost_load_cost=Dict{String,Number}("electricity"=>1e6), lost_emission_cost=Dict{String,Number}("CO2"=>700)) #We set costs for lost electric load of 1,000,000 EUR per MWh and costs for emissions exceeding our emissions limit of 700 EUR per kg-CO2e

slack_opt_config = OptConfig(clust_data,opt_data,optimizer;lost_load_cost=Dict{String,Number}("electricity"=>1e6), lost_emission_cost=Dict{String,Number}("CO2"=>700)) #We set costs for lost electric load of 1,000,000 EUR per MWh and costs for emissions exceeding our emissions limit of 700 EUR per kg-CO2e
slack_result = run_opt(clust_data,opt_data,slack_opt_config)

# Include existing infrastructure at no COST
ex_result = run_opt(ts_clust_data.clust_data,cep_data,optimizer;infrastructure=Dict{String,Array}("existing"=>["all"])) #We add the existing infrastructure of all technologies (no greenfield any more)
ex_opt_config = OptConfig(clust_data,opt_data,optimizer;infrastructure=Dict{String,Array}("existing"=>["all"])) #We add the existing infrastructure of all technologies (no greenfield any more)
ex_result = run_opt(clust_data,opt_data, ex_opt_config)

# Intraday storage (just within each period, same storage level at beginning and end)
simplestor_result = run_opt(ts_clust_data.clust_data,cep_data,optimizer;storage_type="simple")
simplestor_opt_config = OptConfig(clust_data,opt_data,optimizer;storage_type="simple",conversion=true)
simplestor_result = run_opt(clust_data, opt_data, simplestor_opt_config)

# Interday storage (within each period & between the periods)
seasonalstor_result = run_opt(ts_clust_data.clust_data,cep_data,optimizer;storage_type="seasonal")
seasonalstor_opt_config = OptConfig(clust_data,opt_data,optimizer;storage_type="seasonal",conversion=true)
seasonalstor_result = run_opt(clust_data,opt_data,seasonalstor_opt_config)

# Transmission: use "GER_18 to use transmission"
# transmission_result = run_opt(ts_clust_data.clust_data,cep_data,optimizer;transmission=true)

# Segmentation
seg_result = run_opt(ts_seg_data.clust_data,cep_data,optimizer)
# transmission_result = run_opt(clust_data,opt_data,optimizer;transmission=true)

# Desing with clusered data and operation with ts_full_data
# First solve the clustered case
design_result = run_opt(ts_clust_data.clust_data,cep_data,optimizer;limit_emission=Dict{String,Number}("CO2/electricity"=>50))
design_opt_config = OptConfig(clust_data,opt_data,optimizer;limit_emission=Dict{String,Number}("CO2/electricity"=>50))
design_result = run_opt(clust_data,opt_data,design_opt_config)

#capacity_factors
design_variables=get_cep_design_variables(design_result)

# Use the design variable results for the operational run
operation_result = run_opt(ts_input_data,cep_data,design_result.opt_config,design_variables,optimizer;lost_load_cost=Dict{String,Number}("electricity"=>1e6),lost_emission_cost=Dict{String,Number}("CO2"=>700))
operation_opt_config = OptConfig(design_result.config,design_variables;lost_load_cost=Dict{String,Number}("electricity"=>1e6),lost_emission_cost=Dict{String,Number}("CO2"=>700))
operation_result = run_opt(ts_input_data,opt_data,operation_opt_config)

# Change scaling parameters
# Changing the scaling parameters is useful if the data you use represents a much smaller or bigger energy system than the ones representing Germany and California provided in this package
# Determine the right scaling parameters by checking the "real" values of COST, CAP, GEN... (real-VAR) in a solution using your data. Select the scaling parameters to match the following:
# 0.01 ≤ VAR ≤ 100, real-VAR = scale[:VAR] ⋅ VAR
# ⇔ 0.01 ≤ real-VAR / scale[:VAR] ≤ 100
scale=Dict{Symbol,Int}(:COST => 1e9, :CAP => 1e3, :GEN => 1e3, :SLACK => 1e3, :INTRASTOR => 1e3, :INTERSTOR => 1e6, :FLOW => 1e3, :TRANS =>1e3, :LL => 1e6, :LE => 1e9)
co2_result = run_opt(ts_clust_data.clust_data,cep_data,optimizer;scale=scale, limit_emission=Dict{String,Number}("CO2/electricity"=>50))
co2_opt_config = OptConfig(clust_data,opt_data,optimizer;scale=scale, limit_emission=Dict{String,Number}("CO2/electricity"=>50))
co2_result = run_opt(clust_data,opt_data,co2_opt_config)
11 changes: 6 additions & 5 deletions examples/workflow_example_cep_plot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ state="GER_18" # or "GER_18" or "CA_1" or "TX_1"
# laod ts-data
ts_input_data = load_timeseries_data_provided(state; T=24) #CEP
# load cep-data
cep_data = load_cep_data_provided(state)
opt_data = load_cep_data_provided(state)

## CLUSTERING ##
# run aggregation with kmeans
ts_clust_data = run_clust(ts_input_data;method="kmeans",representation="centroid",n_init=100,n_clust=5) # Increase n_init to 10000 for a "real" run
clust_data = ts_clust_data.clust_data
## OPTIMIZATION EXAMPLES##
# select optimizer
optimizer=Clp.Optimizer
# Setup configuration with a co2_limit of 1000
opt_config = OptConfig(clust_data, opt_data, Clp.Optimizer; limit_emission=Dict{String,Number}("CO2/electricity"=>1000))

# Optimize the capacity expansion problem with a co2_limit of 1000
cep = run_opt(ts_clust_data.clust_data,cep_data,optimizer;limit_emission=Dict{String,Number}("CO2/electricity"=>1000))
# Optimize the capacity expansion problem
cep = run_opt(clust_data, opt_data, opt_config)

# Extract the CAP-Variable
cap=cep.variables["CAP"]
Expand Down
5 changes: 3 additions & 2 deletions examples/workflow_introduction.jl
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,10 @@ ts_clust_result_2 = run_clust(ts_input_data; method="kmedoids", representation="
using Clp
optimizer=Clp.Optimizer
# Some extra data for nodes, costs and so on:
cep_data = load_cep_data_provided(ts_clust_data.region)
opt_data = load_cep_data_provided(ts_clust_data.region)
# Running a simple CEP with a co2-limit of 1000 kg/MWh
co2_result = run_opt(ts_clust_data,cep_data,optimizer;descriptor="co2",limit_emission=Dict{String,Number}("CO2/electricity"=>200))
opt_config = OptConfig(ts_clust_data,opt_data,optimizer;descriptor="co2",limit_emission=Dict{String,Number}("CO2/electricity"=>200))
co2_result = run_opt(ts_clust_data, opt_data, opt_config)
# co2_result.
gen_var=co2_result.variables["GEN"]
# get all operating decision variables (GEN)
Expand Down
3 changes: 2 additions & 1 deletion src/CapacityExpansion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ module CapacityExpansion
@reexport using FileIO
using JuMP

export OptDataCEP,
export OptConfig,
OptDataCEP,
OptDataCEPTech,
OptDataCEPNode,
OptDataCEPLine,
Expand Down
30 changes: 15 additions & 15 deletions src/optim_problems/opt.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# Functions to setup the CapacityExpansion optimization model
"""
setup_opt_basic(ts_data::ClustData,opt_data::CEPData,config::Dict{String,Any},optimizer::DataType,optimizer_config::Dict{Symbol,Any})
setup_opt_basic(ts_data::ClustData,opt_data::CEPData,config::OptConfig,optimizer::DataType,optimizer_config::Dict{Symbol,Any})
setting up the basic core elements for a CEP-model
- a JuMP Model is setup and the optimizer is configured. The optimizer itself is passed on as a `optimizer`. It's configuration with `optimizer_config` - Each Symbol and the corresponding value in the Dictionary is passed on to the `with_optimizer` function in addition to the optimizer. For Gurobi an example Dictionary could look like `Dict{Symbol,Any}(:Method => 2, :OutputFlag => 0, :Threads => 2)`
- the sets are setup
"""
function setup_opt_basic(ts_data::ClustData,
opt_data::OptDataCEP,
config::Dict{String,Any},
config::OptConfig,
optimizer::DataType,
optimizer_config::Dict{Symbol,Any})
## MODEL CEP ##
# Initialize model
model = JuMP.Model(with_optimizer(optimizer;optimizer_config...))
# Initialize info
info=[config["descriptor"]]
info=[config.descriptor]
# Setup set
set=setup_opt_set(ts_data, opt_data, config)
# Setup Model CEP
Expand Down Expand Up @@ -569,14 +569,14 @@ function setup_opt_energy_balance_copperplate!(cep::OptModelCEP,
end

"""
setup_opt_limit_emission!(cep::OptModelCEP, ts_data::ClustData, opt_data::OptDataCEP, scale::Dict{Symbol,Int}; limit::Dict{String,Dict} = Dict{String,Dict}("CO2"=>Dict{String,Number}("electricity"=>Inf)), lost_emission_cost::Dict{String,Number} = Dict{String,Number}("CO2"=>Inf))
setup_opt_limit_emission!(cep::OptModelCEP, ts_data::ClustData, opt_data::OptDataCEP, scale::Dict{Symbol,Int}; limit_emission::Dict{String,Dict{String,Number}} = Dict{String,Dict{String,Number}}("CO2"=>Dict{String,Number}("electricity"=>Inf)), lost_emission_cost::Dict{String,Number} = Dict{String,Number}("CO2"=>Inf))
Add emission limits constraints
"""
function setup_opt_limit_emission!(cep::OptModelCEP,
ts_data::ClustData,
opt_data::OptDataCEP,
scale::Dict{Symbol,Int};
limit_emission::Dict{String,Dict}=Dict{String,Dict}(),
limit_emission::Dict{String,Dict{String,Number}}=Dict{String,Dict{String,Number}}(),
lost_emission_cost::Dict{String,Number}=Dict{String,Number}())
## DATA ##
set=cep.set
Expand Down Expand Up @@ -674,16 +674,16 @@ function setup_opt_objective!(cep::OptModelCEP,
end

"""
solve_opt_cep(cep::OptModelCEP,ts_data::ClustData,opt_data::OptDataCEP,config::Dict{String,Any})
solve_opt_cep(cep::OptModelCEP,ts_data::ClustData,opt_data::OptDataCEP,config::OptConfig)
solving the cep model and writing it's results and `limit` into an OptResult-Struct
"""
function solve_opt_cep(cep::OptModelCEP,
ts_data::ClustData,
opt_data::OptDataCEP,
config::Dict{String,Any})
config::OptConfig)
optimize!(cep.model)
status=Symbol(termination_status(cep.model))
scale=config["scale"]
scale=config.scale
objective=objective_value(cep.model)*scale[:COST]
total_demand=get_total_demand(cep,ts_data)
variables=Dict{String,Any}()
Expand All @@ -693,32 +693,32 @@ function solve_opt_cep(cep::OptModelCEP,
variables["GEN"]=OptVariable(cep,:GEN,"ov",scale)
lost_load=0
lost_emission=0
if !isempty(config["lost_load_cost"])
if !isempty(config.lost_load_cost)
variables["SLACK"]=OptVariable(cep,:SLACK,"sv",scale)
variables["LL"]=OptVariable(cep,:LL,"sv",scale)
lost_load=sum(variables["LL"].data)
end
if !isempty(config["lost_emission_cost"])
if !isempty(config.lost_emission_cost)
variables["LE"]=OptVariable(cep,:LE,"sv",scale)
lost_emission=sum(variables["LE"].data)
end
if config["storage"]
if config.model["storage"]
variables["INTRASTOR"]=OptVariable(cep,:INTRASTOR,"ov",scale)
if config["seasonalstorage"]
if config.model["seasonalstorage"]
variables["INTERSTOR"]=OptVariable(cep,:INTERSTOR,"ov",scale)
end
end
if config["transmission"]
if config.model["transmission"]
variables["TRANS"]=OptVariable(cep,:TRANS,"dv",scale)
variables["FLOW"]=OptVariable(cep,:FLOW,"ov",scale)
end
get_met_cap_limit(cep, opt_data, variables)
currency=variables["COST"].axes[2][1]
if lost_load==0 && lost_emission==0
config["print_flag"] && @info("Solved Scenario $(config["descriptor"]): "*String(status)*" min COST: $(round(objective,sigdigits=4)) [$currency] ⇨ $(round(objective/total_demand,sigdigits=4)) [$currency per MWh] s.t. $(text_limit_emission(config["limit_emission"]))")
config.print_flag && @info("Solved Scenario $(config.descriptor): "*String(status)*" min COST: $(round(objective,sigdigits=4)) [$currency] ⇨ $(round(objective/total_demand,sigdigits=4)) [$currency per MWh] s.t. $(text_limit_emission(config.limit_emission))")
else
cost=variables["COST"]
config["print_flag"] && @info("Solved Scenario $(config["descriptor"]): "*String(status)*" min COST: $(round(sum(cost[:,axes(cost,"impact")[1],:]),sigdigits=4)) [$currency] ⇨ $(round(sum(cost[:,axes(cost,"impact")[1],:])/total_demand,sigdigits=4)) [$currency per MWh] with LL: $lost_load [MWh] s.t. $(text_limit_emission(config["limit_emission"])) + $(round(lost_emission/total_demand,sigdigits=4)) (violation) [kg-CO₂-eq. per MWh]")
config.print_flag && @info("Solved Scenario $(config.descriptor): "*String(status)*" min COST: $(round(sum(cost[:,axes(cost,"impact")[1],:]),sigdigits=4)) [$currency] ⇨ $(round(sum(cost[:,axes(cost,"impact")[1],:])/total_demand,sigdigits=4)) [$currency per MWh] with LL: $lost_load [MWh] s.t. $(text_limit_emission(config.limit_emission)) + $(round(lost_emission/total_demand,sigdigits=4)) (violation) [kg-CO₂-eq. per MWh]")
end
info=Dict{String,Any}("total_demand"=>total_demand,"model"=>cep.info,)
return OptResult(status,objective,variables,cep.set,config,info)
Expand Down
Loading