Skip to content

Commit

Permalink
Add balance sense option to the consumer assets (#591)
Browse files Browse the repository at this point in the history
  • Loading branch information
datejada committed Apr 17, 2024
1 parent 7c85666 commit f07b828
Show file tree
Hide file tree
Showing 12 changed files with 480 additions and 469 deletions.
812 changes: 406 additions & 406 deletions benchmark/EU/assets-data.csv

Large diffs are not rendered by default.

6 changes: 4 additions & 2 deletions docs/src/formulation.md
@@ -1,7 +1,7 @@
# [Mathematical Formulation](@id formulation)

This section shows the mathematical formulation of _TulipaEnergyModel.jl_, assuming that the temporal definition of timesteps is the same for all the elements in the model.\
The complete mathematical formulation, including variable temporal resolutions, is also freely available in the [preprint](https://arxiv.org/abs/2309.07711). In addition, the [concepts section](@ref seasonal-storage) has an example of how the model handles the [`flexible time resolution`](@ref flex-time-res).
The complete mathematical formulation, including variable temporal resolutions, is also freely available in the [preprint](https://arxiv.org/pdf/2309.07711.pdf). In addition, the [concepts section](@ref seasonal-storage) has an example of how the model handles the [`flexible time resolution`](@ref flex-time-res).

## [Sets](@id math-sets)

Expand Down Expand Up @@ -152,9 +152,11 @@ v^{\text{flow}}_{f,k,b_k} \geq 0 \quad \forall f \notin \mathcal{F}^{\text{t}},

#### Balance Constraint for Consumers

The balance constraint sense depends on the method selected in the asset file's parameter [`consumer_balance_sense`](@ref assets-data). The default value is $=$, but the user can choose $\geq$ as an option.

```math
\begin{aligned}
\sum_{f \in \mathcal{F}^{\text{in}}_a} v^{\text{flow}}_{f,k,b_k} - \sum_{f \in \mathcal{F}^{\text{out}}_a} v^{\text{flow}}_{f,k,b_k} = p^{\text{demand profile}}_{a,k,b_k} \cdot p^{\text{peak demand}}_{a} \quad
\sum_{f \in \mathcal{F}^{\text{in}}_a} v^{\text{flow}}_{f,k,b_k} - \sum_{f \in \mathcal{F}^{\text{out}}_a} v^{\text{flow}}_{f,k,b_k} \left\{\begin{array}{l} = \\ \geq \end{array}\right\} p^{\text{demand profile}}_{a,k,b_k} \cdot p^{\text{peak demand}}_{a} \quad
\\ \\ \forall a \in \mathcal{A}^{\text{c}}, \forall k \in \mathcal{K},\forall b_k \in \mathcal{B_k}
\end{aligned}
```
Expand Down
4 changes: 2 additions & 2 deletions src/constraints/consumer.jl
Expand Up @@ -27,14 +27,14 @@ function add_consumer_constraints!(
@constraint(
model,
incoming_flow_highest_in_out_resolution[row.index] -
outgoing_flow_highest_in_out_resolution[row.index] ==
outgoing_flow_highest_in_out_resolution[row.index] -
profile_aggregation(
Statistics.mean,
graph[row.asset].rep_periods_profiles,
(:demand, row.rp),
row.timesteps_block,
1.0,
) * graph[row.asset].peak_demand,
) * graph[row.asset].peak_demand in graph[row.asset].consumer_balance_sense,
base_name = "consumer_balance[$(row.asset),$(row.rp),$(row.timesteps_block)]"
) for row in eachrow(df)
]
Expand Down
1 change: 1 addition & 0 deletions src/input-schemas.jl
Expand Up @@ -14,6 +14,7 @@ const schemas = (
:capacity => Float64, # MW
:initial_capacity => Float64, # MW
:peak_demand => Float64, # MW
:consumer_balance_sense => Union{Missing,Symbol}, # Sense of the consumer balance constraint (default ==)
:is_seasonal => Bool, # Whether seasonal storage (e.g. hydro) or not (e.g. battery)
:storage_inflows => Float64, # MWh/year
:initial_storage_capacity => Float64, # MWh
Expand Down
5 changes: 5 additions & 0 deletions src/io.jl
Expand Up @@ -137,6 +137,11 @@ function create_graph_and_representative_periods_from_csv_folder(
row.capacity,
row.initial_capacity,
row.peak_demand,
if !ismissing(row.consumer_balance_sense) && row.consumer_balance_sense == :>=
MathOptInterface.GreaterThan(0.0)
else
MathOptInterface.EqualTo(0.0)
end,
row.is_seasonal,
row.storage_inflows,
row.initial_storage_capacity,
Expand Down
3 changes: 3 additions & 0 deletions src/structures.jl
Expand Up @@ -39,6 +39,7 @@ mutable struct GraphAssetData
capacity::Float64
initial_capacity::Float64
peak_demand::Float64
consumer_balance_sense::Union{MathOptInterface.EqualTo,MathOptInterface.GreaterThan}
is_seasonal::Bool
storage_inflows::Union{Missing,Float64}
initial_storage_capacity::Float64
Expand All @@ -63,6 +64,7 @@ mutable struct GraphAssetData
capacity,
initial_capacity,
peak_demand,
consumer_balance_sense,
is_seasonal,
storage_inflows,
initial_storage_capacity,
Expand All @@ -82,6 +84,7 @@ mutable struct GraphAssetData
capacity,
initial_capacity,
peak_demand,
consumer_balance_sense,
is_seasonal,
storage_inflows,
initial_storage_capacity,
Expand Down
48 changes: 24 additions & 24 deletions test/inputs/Norse/assets-data.csv
@@ -1,24 +1,24 @@
,{producer;consumer;storage;hub;conversion},{true;false},{true;false},{true;false},kEUR/MW/year,MW,MW,MW,MW,{true;false},MWh/year,MWh,MWh,h
name,type,active,investable,investment_integer,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,is_seasonal,storage_inflows,initial_storage_capacity,initial_storage_level,energy_to_power_ratio
Asgard_Battery,storage,true,true,true,300,,10,0,0,false,0,0,,100
Asgard_Solar,producer,true,true,true,350,50000,100,0,0,false,0,0,0,0
Asgard_E_demand,consumer,true,false,false,0,0,0,0,65787.17792,false,0,0,0,0
Asgard_CCGT,conversion,true,true,true,650,,500,0,0,false,0,0,0,0
G_imports,producer,true,false,false,0,0,0,75000,0,false,0,0,0,0
Midgard_Wind,producer,true,true,true,1300,80000,3,0,0,false,0,0,0,0
Midgard_Hydro,storage,true,false,false,1600,0,0,250,0,true,10000,50000,25000,0
Midgard_Nuclear_SMR,producer,true,true,false,6000,,150,1000,0,false,0,0,0,0
Midgard_E_imports,producer,true,false,false,0,0,0,500,0,false,0,0,0,0
Midgard_CCGT,conversion,true,true,true,650,,500,0,0,false,0,0,0,0
Midgard_E_demand,consumer,true,false,false,0,0,0,0,19604.76443,false,0,0,0,0
Valhalla_H2_generator,conversion,true,true,true,479,,100,0,0,false,0,0,0,0
Valhalla_H2_storage,storage,true,true,true,0.1,,500,0,0,true,0,0,,10000
Valhalla_H2_demand,consumer,true,false,false,0,0,0,0,745.735,false,0,0,0,0
Valhalla_Electrolyser,conversion,true,true,true,1260,,100,500,0,false,0,0,0,0
Valhalla_E_balance,hub,true,false,false,0,0,0,0,0,false,0,0,0,0
Valhalla_E_exports,consumer,true,false,false,0,0,0,0,50,false,0,0,0,0
Valhalla_Fuel_cell,conversion,true,true,true,800,,100,0,0,false,0,0,0,0
Valhalla_Heat_pump,conversion,true,true,true,300,,100,0,0,false,0,0,0,0
Valhalla_Waste_heat,producer,true,false,false,1450,,200,0,0,false,0,0,0,0
Valhalla_Heat_demand,consumer,true,false,false,0,0,0,0,3548.42445,false,0,0,0,0
Valhalla_GT,conversion,true,true,true,400,100000,500,0,0,false,0,0,0,0
,{producer;consumer;storage;hub;conversion},{true;false},{true;false},{true;false},kEUR/MW/year,MW,MW,MW,MW,{empty;==;>=},{true;false},MWh/year,MWh,MWh,h
name,type,active,investable,investment_integer,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,consumer_balance_sense,is_seasonal,storage_inflows,initial_storage_capacity,initial_storage_level,energy_to_power_ratio
Asgard_Battery,storage,true,true,true,300,,10,0,0,,false,0,0,,100
Asgard_Solar,producer,true,true,true,350,50000,100,0,0,,false,0,0,0,0
Asgard_E_demand,consumer,true,false,false,0,0,0,0,65787.17792,,false,0,0,0,0
Asgard_CCGT,conversion,true,true,true,650,,500,0,0,,false,0,0,0,0
G_imports,producer,true,false,false,0,0,0,75000,0,,false,0,0,0,0
Midgard_Wind,producer,true,true,true,1300,80000,3,0,0,,false,0,0,0,0
Midgard_Hydro,storage,true,false,false,1600,0,0,250,0,,true,10000,50000,25000,0
Midgard_Nuclear_SMR,producer,true,true,false,6000,,150,1000,0,,false,0,0,0,0
Midgard_E_imports,producer,true,false,false,0,0,0,500,0,,false,0,0,0,0
Midgard_CCGT,conversion,true,true,true,650,,500,0,0,,false,0,0,0,0
Midgard_E_demand,consumer,true,false,false,0,0,0,0,19604.76443,,false,0,0,0,0
Valhalla_H2_generator,conversion,true,true,true,479,,100,0,0,,false,0,0,0,0
Valhalla_H2_storage,storage,true,true,true,0.1,,500,0,0,,true,0,0,,10000
Valhalla_H2_demand,consumer,true,false,false,0,0,0,0,745.735,,false,0,0,0,0
Valhalla_Electrolyser,conversion,true,true,true,1260,,100,500,0,,false,0,0,0,0
Valhalla_E_balance,hub,true,false,false,0,0,0,0,0,,false,0,0,0,0
Valhalla_E_exports,consumer,true,false,false,0,0,0,0,50,,false,0,0,0,0
Valhalla_Fuel_cell,conversion,true,true,true,800,,100,0,0,,false,0,0,0,0
Valhalla_Heat_pump,conversion,true,true,true,300,,100,0,0,,false,0,0,0,0
Valhalla_Waste_heat,producer,true,false,false,1450,,200,0,0,,false,0,0,0,0
Valhalla_Heat_demand,consumer,true,false,false,0,0,0,0,3548.42445,>=,false,0,0,0,0
Valhalla_GT,conversion,true,true,true,400,100000,500,0,0,,false,0,0,0,0
20 changes: 10 additions & 10 deletions test/inputs/Storage/assets-data.csv
@@ -1,10 +1,10 @@
,{producer;consumer;storage;hub;conversion},{true;false},{true;false},{true;false},kEUR/MW/year,MW,MW,MW,MW,{true;false},MWh/year,MWh,MWh,h
name,type,active,investable,investment_integer,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,is_seasonal,storage_inflows,initial_storage_capacity,initial_storage_level,energy_to_power_ratio
ocgt,producer,true,false,false,0,,100,100,0,false,0,0,0,0
ccgt,producer,true,false,false,0,,400,800,0,false,0,0,0,0
wind,producer,true,false,false,0,,50,1750,0,false,0,0,0,0
solar,producer,true,false,false,0,,10,450,0,false,0,0,0,0
ens,producer,true,false,false,0,,0,1240,0,false,0,0,0,0
battery,storage,true,false,false,0,,10,10,0,false,0,20,0,0
phs,storage,true,false,false,0,,100,100,0,true,0,4800,2400,0
demand,consumer,true,false,false,0,,0,0,1240,false,0,0,0,0
,{producer;consumer;storage;hub;conversion},{true;false},{true;false},{true;false},kEUR/MW/year,MW,MW,MW,MW,{empty;==;>=},{true;false},MWh/year,MWh,MWh,h
name,type,active,investable,investment_integer,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,consumer_balance_sense,is_seasonal,storage_inflows,initial_storage_capacity,initial_storage_level,energy_to_power_ratio
ocgt,producer,true,false,false,0,,100,100,0,,false,0,0,0,0
ccgt,producer,true,false,false,0,,400,800,0,,false,0,0,0,0
wind,producer,true,false,false,0,,50,1750,0,,false,0,0,0,0
solar,producer,true,false,false,0,,10,450,0,,false,0,0,0,0
ens,producer,true,false,false,0,,0,1240,0,,false,0,0,0,0
battery,storage,true,false,false,0,,10,10,0,,false,0,20,0,0
phs,storage,true,false,false,0,,100,100,0,,true,0,4800,2400,0
demand,consumer,true,false,false,0,,0,0,1240,,false,0,0,0,0
16 changes: 8 additions & 8 deletions test/inputs/Tiny/assets-data.csv
@@ -1,8 +1,8 @@
,{producer;consumer},{true;false},{true;false},{true;false},kEUR/MW/year,MW,MW,MW,MW,{true;false},MWh/year,MWh,MWh,h
name,type,active,investable,investment_integer,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,is_seasonal,storage_inflows,initial_storage_capacity,initial_storage_level,energy_to_power_ratio
ocgt,producer,true,true,true,25,,100,0,0,false,0,0,0,0
ccgt,producer,true,true,true,40,10000,400,0,0,false,0,0,0,0
wind,producer,true,true,true,70,,50,0,0,false,0,0,0,0
solar,producer,true,true,true,50,,10,0,0,false,0,0,0,0
ens,producer,true,false,false,0,,0,1115,0,false,0,0,0,0
demand,consumer,true,false,false,0,,0,0,1115,false,0,0,0,0
,{producer;consumer},{true;false},{true;false},{true;false},kEUR/MW/year,MW,MW,MW,MW,{empty;==;>=},{true;false},MWh/year,MWh,MWh,h
name,type,active,investable,investment_integer,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,consumer_balance_sense,is_seasonal,storage_inflows,initial_storage_capacity,initial_storage_level,energy_to_power_ratio
ocgt,producer,true,true,true,25,,100,0,0,,false,0,0,0,0
ccgt,producer,true,true,true,40,10000,400,0,0,,false,0,0,0,0
wind,producer,true,true,true,70,,50,0,0,,false,0,0,0,0
solar,producer,true,true,true,50,,10,0,0,,false,0,0,0,0
ens,producer,true,false,false,0,,0,1115,0,,false,0,0,0,0
demand,consumer,true,false,false,0,,0,0,1115,,false,0,0,0,0
16 changes: 8 additions & 8 deletions test/inputs/Tiny/bad-assets-data.csv
@@ -1,8 +1,8 @@
,,{producer;consumer},{true;false},{true;false},{true;false},kEUR/MWh,kEUR/MW/year,MW,MW,MW,MW,{true;false},MWh/year,MWh,h
iid,name,type,active,investable,investment_integer,variable_cost,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,is_seasonal,storage_inflows,initial_storage_capacity,energy_to_power_ratio
1,ocgt,producer,true,t,true,0.07,25,,100,0,t,false,,0,0
2,ccgt,producer,true,true,true,0.05,40,,400,0,0,false,,0,0
3,wind,producer,true,true,true,0.001,70,,50,0,0,false,,0,0
4,solar,producer,true,true,true,0,50,,10,0,0,false,,0,0
5,ens,producer,true,false,false,0.18,0,,0,0,0,false,,0,0
6,demand,consumer,true,false,false,0,0,,0,0,1115,false,,0,0
,,{producer;consumer},{true;false},{true;false},{true;false},kEUR/MWh,kEUR/MW/year,MW,MW,MW,MW,{empty;==;>=},{true;false},MWh/year,MWh,h
iid,name,type,active,investable,investment_integer,variable_cost,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,consumer_balance_sense,is_seasonal,storage_inflows,initial_storage_capacity,energy_to_power_ratio
1,ocgt,producer,true,t,true,0.07,25,,100,0,t,,false,,0,0
2,ccgt,producer,true,true,true,0.05,40,,400,0,0,,false,,0,0
3,wind,producer,true,true,true,0.001,70,,50,0,0,,false,,0,0
4,solar,producer,true,true,true,0,50,,10,0,0,,false,,0,0
5,ens,producer,true,false,false,0.18,0,,0,0,0,,false,,0,0
6,demand,consumer,true,false,false,0,0,,0,0,1115,,false,,0,0
16 changes: 8 additions & 8 deletions test/inputs/Variable Resolution/assets-data.csv
@@ -1,8 +1,8 @@
,{producer;consumer},{true;false},{true;false},{true;false},kEUR/MW/year,MW,MW,MW,MW,{true;false},MWh/year,MWh,MWh,h
name,type,active,investable,investment_integer,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,is_seasonal,storage_inflows,initial_storage_capacity,initial_storage_level,energy_to_power_ratio
H2,producer,true,false,false,0,,400,400,0,false,0,0,0,0
ccgt,conversion,true,false,false,0,,100,100,0,false,0,0,0,0
wind,producer,true,false,false,0,,50,100,0,false,0,0,0,0
phs,storage,true,false,false,0,,25,25,0,false,0,150,0,6
demand,consumer,true,false,false,0,,0,0,100,false,0,0,0,0
balance,hub,true,false,false,0,,0,0,0,false,0,0,0,0
,{producer;consumer},{true;false},{true;false},{true;false},kEUR/MW/year,MW,MW,MW,MW,{empty;==;>=},{true;false},MWh/year,MWh,MWh,h
name,type,active,investable,investment_integer,investment_cost,investment_limit,capacity,initial_capacity,peak_demand,consumer_balance_sense,is_seasonal,storage_inflows,initial_storage_capacity,initial_storage_level,energy_to_power_ratio
H2,producer,true,false,false,0,,400,400,0,,false,0,0,0,0
ccgt,conversion,true,false,false,0,,100,100,0,,false,0,0,0,0
wind,producer,true,false,false,0,,50,100,0,,false,0,0,0,0
phs,storage,true,false,false,0,,25,25,0,,false,0,150,0,6
demand,consumer,true,false,false,0,,0,0,100,,false,0,0,0,0
balance,hub,true,false,false,0,,0,0,0,,false,0,0,0,0
2 changes: 1 addition & 1 deletion test/test-case-studies.jl
Expand Up @@ -2,7 +2,7 @@
dir = joinpath(INPUT_FOLDER, "Norse")
parameters_dict = Dict(
HiGHS.Optimizer => Dict("mip_rel_gap" => 0.0, "output_flag" => false),
GLPK.Optimizer => Dict("mip_gap" => 0.0, "msg_lev" => 0),
GLPK.Optimizer => Dict("mip_gap" => 0.0, "msg_lev" => 0, "presolve" => GLPK.GLP_ON),
)
if !Sys.isapple()
parameters_dict[Cbc.Optimizer] = Dict("ratioGap" => 0.0, "logLevel" => 0)
Expand Down

0 comments on commit f07b828

Please sign in to comment.