diff --git a/src/constraints/capacity.jl b/src/constraints/capacity.jl index c7ada26a..99642372 100644 --- a/src/constraints/capacity.jl +++ b/src/constraints/capacity.jl @@ -34,8 +34,10 @@ function add_capacity_constraints!(model, variables, constraints, graph, sets) flow = variables[:flow].container ## unpack from constraints - incoming_flow_highest_in_resolution = constraints[:highest_in].expressions[:incoming] - outgoing_flow_highest_out_resolution = constraints[:highest_out].expressions[:outgoing] + cons_incoming = constraints[:capacity_incoming] + cons_outgoing = constraints[:capacity_outgoing] + incoming_flow = cons_incoming.expressions[:incoming] + outgoing_flow = cons_outgoing.expressions[:outgoing] ## Expressions used by capacity constraints # - Create capacity limit for outgoing flows @@ -77,7 +79,7 @@ function add_capacity_constraints!(model, variables, constraints, graph, sets) graph[row.asset].capacity * accumulated_units[accumulated_units_lookup[(row.asset, row.year)]] ) - end for row in eachrow(constraints[:highest_out].indices) + end for row in eachrow(cons_outgoing.indices) ] # - Create accumulated investment limit for the use of binary storage method with investments @@ -123,10 +125,8 @@ function add_capacity_constraints!(model, variables, constraints, graph, sets) (graph[row.asset].capacity * accumulated_initial_units[row.asset, row.year]) * (1 - is_charging) ) - end for (row, is_charging) in zip( - eachrow(constraints[:highest_out].indices), - constraints[:highest_out].expressions[:is_charging], - ) + end for (row, is_charging) in + zip(eachrow(cons_outgoing.indices), cons_outgoing.expressions[:is_charging]) ] assets_profile_times_capacity_out_with_binary_part2 = @@ -149,10 +149,8 @@ function add_capacity_constraints!(model, variables, constraints, graph, sets) ) ) ) - end for (row, is_charging) in zip( - eachrow(constraints[:highest_out].indices), - constraints[:highest_out].expressions[:is_charging], - ) + end for (row, is_charging) in + zip(eachrow(cons_outgoing.indices), cons_outgoing.expressions[:is_charging]) ] # - Create capacity limit for incoming flows @@ -171,7 +169,7 @@ function add_capacity_constraints!(model, variables, constraints, graph, sets) ) * graph[row.asset].capacity * accumulated_units[accumulated_units_lookup[(row.asset, row.year)]] - ) for row in eachrow(constraints[:highest_in].indices) + ) for row in eachrow(cons_incoming.indices) ] # - Create capacity limit for incoming flows with binary is_charging for storage assets @@ -210,10 +208,8 @@ function add_capacity_constraints!(model, variables, constraints, graph, sets) (graph[row.asset].capacity * accumulated_initial_units[row.asset, row.year]) * is_charging ) - end for (row, is_charging) in zip( - eachrow(constraints[:highest_in].indices), - constraints[:highest_in].expressions[:is_charging], - ) + end for (row, is_charging) in + zip(eachrow(cons_incoming.indices), cons_incoming.expressions[:is_charging]) ] assets_profile_times_capacity_in_with_binary_part2 = @@ -236,40 +232,36 @@ function add_capacity_constraints!(model, variables, constraints, graph, sets) ) ) ) - end for (row, is_charging) in zip( - eachrow(constraints[:highest_in].indices), - constraints[:highest_in].expressions[:is_charging], - ) + end for (row, is_charging) in + zip(eachrow(cons_incoming.indices), cons_incoming.expressions[:is_charging]) ] ## Capacity limit constraints (using the highest resolution) # - Maximum output flows limit attach_constraint!( model, - constraints[:highest_out], + cons_outgoing, :max_output_flows_limit, [ @constraint( model, - outgoing_flow_highest_out_resolution[row.index] ≤ - assets_profile_times_capacity_out[row.index], + outgoing_flow[row.index] ≤ assets_profile_times_capacity_out[row.index], base_name = "max_output_flows_limit[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(constraints[:highest_out].indices) + ) for row in eachrow(cons_outgoing.indices) ], ) # - Maximum input flows limit attach_constraint!( model, - constraints[:highest_in], + cons_incoming, :max_input_flows_limit, [ @constraint( model, - incoming_flow_highest_in_resolution[row.index] ≤ - assets_profile_times_capacity_in[row.index], + incoming_flow[row.index] ≤ assets_profile_times_capacity_in[row.index], base_name = "max_input_flows_limit[$(row.asset),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(constraints[:highest_in].indices) + ) for row in eachrow(cons_incoming.indices) ], ) @@ -278,43 +270,41 @@ function add_capacity_constraints!(model, variables, constraints, graph, sets) model[:max_output_flows_limit_with_binary_part1] = [ @constraint( model, - outgoing_flow_highest_out_resolution[row.index] ≤ + outgoing_flow[row.index] ≤ assets_profile_times_capacity_out_with_binary_part1[row.index], base_name = "max_output_flows_limit_with_binary_part1[$(row.asset),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(constraints[:highest_out].indices) if - row.asset ∈ Asb && outgoing_flow_highest_out_resolution[row.index] != 0 + ) for row in eachrow(cons_outgoing.indices) if + row.asset ∈ Asb && outgoing_flow[row.index] != 0 ] model[:max_output_flows_limit_with_binary_part2] = [ @constraint( model, - outgoing_flow_highest_out_resolution[row.index] ≤ + outgoing_flow[row.index] ≤ assets_profile_times_capacity_out_with_binary_part2[row.index], base_name = "max_output_flows_limit_with_binary_part2[$(row.asset),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(constraints[:highest_out].indices) if row.asset ∈ Ai[row.year] && - row.asset ∈ Asb && - outgoing_flow_highest_out_resolution[row.index] != 0 + ) for row in eachrow(cons_outgoing.indices) if + row.asset ∈ Ai[row.year] && row.asset ∈ Asb && outgoing_flow[row.index] != 0 ] # - Maximum input flows limit with is_charging binary for storage assets model[:max_input_flows_limit_with_binary_part1] = [ @constraint( model, - incoming_flow_highest_in_resolution[row.index] ≤ + incoming_flow[row.index] ≤ assets_profile_times_capacity_in_with_binary_part1[row.index], base_name = "max_input_flows_limit_with_binary_part1[$(row.asset),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(constraints[:highest_in].indices) if - row.asset ∈ Asb && incoming_flow_highest_in_resolution[row.index] != 0 + ) for row in eachrow(cons_incoming.indices) if + row.asset ∈ Asb && incoming_flow[row.index] != 0 ] model[:max_input_flows_limit_with_binary_part2] = [ @constraint( model, - incoming_flow_highest_in_resolution[row.index] ≤ + incoming_flow[row.index] ≤ assets_profile_times_capacity_in_with_binary_part2[row.index], base_name = "max_input_flows_limit_with_binary_part2[$(row.asset),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(constraints[:highest_in].indices) if row.asset ∈ Ai[row.year] && - row.asset ∈ Asb && - incoming_flow_highest_in_resolution[row.index] != 0 + ) for row in eachrow(cons_incoming.indices) if + row.asset ∈ Ai[row.year] && row.asset ∈ Asb && incoming_flow[row.index] != 0 ] # - Lower limit for flows associated with assets diff --git a/src/constraints/consumer.jl b/src/constraints/consumer.jl index da186088..68376b45 100644 --- a/src/constraints/consumer.jl +++ b/src/constraints/consumer.jl @@ -12,13 +12,12 @@ add_consumer_constraints!(model, Adds the consumer asset constraints to the model. """ -function add_consumer_constraints!(model, constraints, graph, sets) - Ac = sets[:Ac] - incoming_flow_highest_in_out_resolution = constraints[:highest_in_out].expressions[:incoming] - outgoing_flow_highest_in_out_resolution = constraints[:highest_in_out].expressions[:outgoing] +function add_consumer_constraints!(model, constraints, graph) + cons = constraints[:balance_consumer] + incoming_flow_highest_in_out_resolution = cons.expressions[:incoming] + outgoing_flow_highest_in_out_resolution = cons.expressions[:outgoing] # - Balance constraint (using the lowest temporal resolution) - df = filter(:asset => ∈(Ac), constraints[:highest_in_out].indices; view = true) model[:consumer_balance] = [ @constraint( model, @@ -35,6 +34,6 @@ function add_consumer_constraints!(model, constraints, graph, sets) ) * graph[row.asset].peak_demand[row.year] in graph[row.asset].consumer_balance_sense, base_name = "consumer_balance[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(df) + ) for row in eachrow(cons.indices) ] end diff --git a/src/constraints/conversion.jl b/src/constraints/conversion.jl index 99e2d085..78066ecd 100644 --- a/src/constraints/conversion.jl +++ b/src/constraints/conversion.jl @@ -11,17 +11,16 @@ add_conversion_constraints!(model, Adds the conversion asset constraints to the model. """ -function add_conversion_constraints!(model, constraints, sets) - Acv = sets[:Acv] +function add_conversion_constraints!(model, constraints) # - Balance constraint (using the lowest temporal resolution) - df = filter(:asset => ∈(Acv), constraints[:lowest].indices; view = true) - incoming = constraints[:lowest].expressions[:incoming] - outgoing = constraints[:lowest].expressions[:outgoing] + cons = constraints[:balance_conversion] + incoming = cons.expressions[:incoming] + outgoing = cons.expressions[:outgoing] model[:conversion_balance] = [ @constraint( model, incoming[row.index] == outgoing[row.index], base_name = "conversion_balance[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(df) + ) for row in eachrow(cons.indices) ] end diff --git a/src/constraints/create.jl b/src/constraints/create.jl index e5996066..c40a5f98 100644 --- a/src/constraints/create.jl +++ b/src/constraints/create.jl @@ -6,15 +6,17 @@ function compute_constraints_indices(connection) constraints = Dict{Symbol,TulipaConstraint}( key => TulipaConstraint(connection, "cons_$key") for key in ( - :lowest, - :highest_in_out, - :highest_in, - :highest_out, - :units_on_and_outflows, - :storage_level_intra_rp, - :storage_level_inter_rp, - :min_energy_inter_rp, - :max_energy_inter_rp, + :balance_conversion, + :balance_consumer, + :balance_hub, + :capacity_incoming, + :capacity_outgoing, + :ramping_with_unit_commitment, + :ramping_without_unit_commitment, + :balance_storage_rep_period, + :balance_storage_over_clustered_year, + :min_energy_over_clustered_year, + :max_energy_over_clustered_year, ) ) @@ -25,7 +27,7 @@ function _create_constraints_tables(connection) DuckDB.query( connection, "CREATE OR REPLACE TEMP SEQUENCE id START 1; - CREATE OR REPLACE TABLE cons_lowest AS + CREATE OR REPLACE TABLE cons_balance_conversion AS SELECT nextval('id') AS index, asset.asset, @@ -37,7 +39,7 @@ function _create_constraints_tables(connection) LEFT JOIN asset ON t_low.asset = asset.asset WHERE - asset.type in ('conversion', 'producer') + asset.type in ('conversion') ORDER BY asset.asset, t_low.year, @@ -49,7 +51,7 @@ function _create_constraints_tables(connection) DuckDB.query( connection, "CREATE OR REPLACE TEMP SEQUENCE id START 1; - CREATE OR REPLACE TABLE cons_highest_in_out AS + CREATE OR REPLACE TABLE cons_balance_consumer AS SELECT nextval('id') AS index, t_high.* @@ -62,13 +64,34 @@ function _create_constraints_tables(connection) AND t_high.year = asset_both.commission_year WHERE asset_both.active = true - AND asset.type in ('hub', 'consumer')", + AND asset.type = 'consumer'; + ", + ) + + DuckDB.query( + connection, + "CREATE OR REPLACE TEMP SEQUENCE id START 1; + CREATE OR REPLACE TABLE cons_balance_hub AS + SELECT + nextval('id') AS index, + t_high.* + FROM t_highest_all_flows AS t_high + LEFT JOIN asset + ON t_high.asset = asset.asset + LEFT JOIN asset_both + ON t_high.asset = asset_both.asset + AND t_high.year = asset_both.milestone_year + AND t_high.year = asset_both.commission_year + WHERE + asset_both.active = true + AND asset.type = 'hub'; + ", ) DuckDB.query( connection, "CREATE OR REPLACE TEMP SEQUENCE id START 1; - CREATE OR REPLACE TABLE cons_highest_in AS + CREATE OR REPLACE TABLE cons_capacity_incoming AS SELECT nextval('id') AS index, t_high.* @@ -87,7 +110,7 @@ function _create_constraints_tables(connection) DuckDB.query( connection, "CREATE OR REPLACE TEMP SEQUENCE id START 1; - CREATE OR REPLACE TABLE cons_highest_out AS + CREATE OR REPLACE TABLE cons_capacity_outgoing AS SELECT nextval('id') AS index, t_high.* @@ -106,7 +129,7 @@ function _create_constraints_tables(connection) DuckDB.query( connection, "CREATE OR REPLACE TEMP SEQUENCE id START 1; - CREATE OR REPLACE TABLE cons_units_on_and_outflows AS + CREATE OR REPLACE TABLE cons_ramping_with_unit_commitment AS SELECT nextval('id') AS index, t_high.* @@ -126,14 +149,33 @@ function _create_constraints_tables(connection) DuckDB.query( connection, - "CREATE OR REPLACE TABLE cons_storage_level_intra_rp AS + "CREATE OR REPLACE TEMP SEQUENCE id START 1; + CREATE OR REPLACE TABLE cons_ramping_without_unit_commitment AS + SELECT + nextval('id') AS index, + t_high.* + FROM t_highest_out_flows AS t_high + LEFT JOIN asset + ON t_high.asset = asset.asset + LEFT JOIN asset_both + ON t_high.asset = asset_both.asset + AND t_high.year = asset_both.milestone_year + AND t_high.year = asset_both.commission_year + WHERE + asset_both.active = true + AND asset.type in ('producer', 'storage', 'conversion')", + ) + + DuckDB.query( + connection, + "CREATE OR REPLACE TABLE cons_balance_storage_rep_period AS SELECT * FROM var_storage_level_intra_rp ", ) DuckDB.query( connection, - "CREATE OR REPLACE TABLE cons_storage_level_inter_rp AS + "CREATE OR REPLACE TABLE cons_balance_storage_over_clustered_year AS SELECT * FROM var_storage_level_inter_rp ", ) @@ -141,7 +183,7 @@ function _create_constraints_tables(connection) DuckDB.query( connection, "CREATE OR REPLACE TEMP SEQUENCE id START 1; - CREATE OR REPLACE TABLE cons_min_energy_inter_rp AS + CREATE OR REPLACE TABLE cons_min_energy_over_clustered_year AS SELECT nextval('id') AS index, attr.asset, @@ -162,7 +204,7 @@ function _create_constraints_tables(connection) DuckDB.query( connection, "CREATE OR REPLACE TEMP SEQUENCE id START 1; - CREATE OR REPLACE TABLE cons_max_energy_inter_rp AS + CREATE OR REPLACE TABLE cons_max_energy_over_clustered_year AS SELECT nextval('id') AS index, attr.asset, diff --git a/src/constraints/energy.jl b/src/constraints/energy.jl index 39dbdee3..b78c7378 100644 --- a/src/constraints/energy.jl +++ b/src/constraints/energy.jl @@ -11,10 +11,10 @@ function add_energy_constraints!(model, constraints, graph) ## INTER-TEMPORAL CONSTRAINTS (between representative periods) # - Maximum outgoing energy within each period block - model[:max_energy_inter_rp] = [ + model[:max_energy_over_clustered_year] = [ @constraint( model, - constraints[:max_energy_inter_rp].expressions[:outgoing][row.index] ≤ + constraints[:max_energy_over_clustered_year].expressions[:outgoing][row.index] ≤ profile_aggregation( sum, graph[row.asset].timeframe_profiles, @@ -24,15 +24,15 @@ function add_energy_constraints!(model, constraints, graph) row.period_block_start:row.period_block_end, 1.0, ) * (graph[row.asset].max_energy_timeframe_partition[row.year]), - base_name = "max_energy_inter_rp_limit[$(row.asset),$(row.year),$(row.period_block_start):$(row.period_block_end)]" - ) for row in eachrow(constraints[:max_energy_inter_rp].indices) + base_name = "max_energy_over_clustered_year_limit[$(row.asset),$(row.year),$(row.period_block_start):$(row.period_block_end)]" + ) for row in eachrow(constraints[:max_energy_over_clustered_year].indices) ] # - Minimum outgoing energy within each period block - model[:min_energy_inter_rp] = [ + model[:min_energy_over_clustered_year] = [ @constraint( model, - constraints[:min_energy_inter_rp].expressions[:outgoing][row.index] ≥ + constraints[:min_energy_over_clustered_year].expressions[:outgoing][row.index] ≥ profile_aggregation( sum, graph[row.asset].timeframe_profiles, @@ -42,7 +42,7 @@ function add_energy_constraints!(model, constraints, graph) row.period_block_start:row.period_block_end, 1.0, ) * (graph[row.asset].min_energy_timeframe_partition[row.year]), - base_name = "min_energy_inter_rp_limit[$(row.asset),$(row.year),$(row.period_block_start):$(row.period_block_end)]" - ) for row in eachrow(constraints[:min_energy_inter_rp].indices) + base_name = "min_energy_over_clustered_year_limit[$(row.asset),$(row.year),$(row.period_block_start):$(row.period_block_end)]" + ) for row in eachrow(constraints[:min_energy_over_clustered_year].indices) ] end diff --git a/src/constraints/hub.jl b/src/constraints/hub.jl index ee51e061..d9b3f64a 100644 --- a/src/constraints/hub.jl +++ b/src/constraints/hub.jl @@ -11,18 +11,18 @@ add_hub_constraints!(model, Adds the hub asset constraints to the model. """ -function add_hub_constraints!(model, constraints, sets) - Ah = sets[:Ah] - incoming_flow_highest_in_out_resolution = constraints[:highest_in_out].expressions[:incoming] - outgoing_flow_highest_in_out_resolution = constraints[:highest_in_out].expressions[:outgoing] +function add_hub_constraints!(model, constraints) + cons = constraints[:balance_hub] + incoming_flow_highest_in_out_resolution = cons.expressions[:incoming] + outgoing_flow_highest_in_out_resolution = cons.expressions[:outgoing] + # - Balance constraint (using the lowest temporal resolution) - df = filter(:asset => ∈(Ah), constraints[:highest_in_out].indices; view = true) model[:hub_balance] = [ @constraint( model, incoming_flow_highest_in_out_resolution[row.index] == outgoing_flow_highest_in_out_resolution[row.index], base_name = "hub_balance[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(df) + ) for row in eachrow(cons.indices) ] end diff --git a/src/constraints/ramping-and-unit-commitment.jl b/src/constraints/ramping-and-unit-commitment.jl index c034e0db..f4247396 100644 --- a/src/constraints/ramping-and-unit-commitment.jl +++ b/src/constraints/ramping-and-unit-commitment.jl @@ -16,7 +16,9 @@ function add_ramping_constraints!(model, variables, constraints, graph, sets) accumulated_units = model[:accumulated_units] ## unpack from constraints - outgoing_flow_highest_out_resolution = constraints[:highest_out].expressions[:outgoing] + cons_with = constraints[:ramping_with_unit_commitment] + cons_without = constraints[:ramping_without_unit_commitment] + outgoing_flow = cons_without.expressions[:outgoing] ## Expressions used by the ramping and unit commitment constraints # - Expression to have the product of the profile and the capacity paramters @@ -32,8 +34,7 @@ function add_ramping_constraints!(model, variables, constraints, graph, sets) row.time_block_start:row.time_block_end, 1.0, ) * graph[row.asset].capacity - ) for row in eachrow(constraints[:units_on_and_outflows].indices) if - is_active(graph, row.asset, row.year) + ) for row in eachrow(cons_with.indices) if is_active(graph, row.asset, row.year) ] # - Flow that is above the minimum operating point of the asset @@ -41,11 +42,11 @@ function add_ramping_constraints!(model, variables, constraints, graph, sets) model[:flow_above_min_operating_point] = [ @expression( model, - constraints[:units_on_and_outflows].expressions[:outgoing][row.index] - + cons_with.expressions[:outgoing][row.index] - profile_times_capacity[row.index] * graph[row.asset].min_operating_point * - constraints[:units_on_and_outflows].expressions[:units_on][row.index] - ) for row in eachrow(constraints[:units_on_and_outflows].indices) + cons_with.expressions[:units_on][row.index] + ) for row in eachrow(cons_with.indices) ] ## Unit Commitment Constraints (basic implementation - more advanced will be added in 2025) @@ -63,21 +64,21 @@ function add_ramping_constraints!(model, variables, constraints, graph, sets) # - Minimum output flow above the minimum operating point attach_constraint!( model, - constraints[:units_on_and_outflows], + cons_with, :min_output_flow_with_unit_commitment, [ @constraint( model, flow_above_min_operating_point[row.index] ≥ 0, base_name = "min_output_flow_with_unit_commitment[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for row in eachrow(constraints[:units_on_and_outflows].indices) + ) for row in eachrow(cons_with.indices) ], ) # - Maximum output flow above the minimum operating point attach_constraint!( model, - constraints[:units_on_and_outflows], + cons_with, :max_output_flow_with_unit_commitment, [ @constraint( @@ -85,23 +86,19 @@ function add_ramping_constraints!(model, variables, constraints, graph, sets) flow_above_min_operating_point[row.index] ≤ (1 - graph[row.asset].min_operating_point) * profile_times_capacity[row.index] * - constraints[:units_on_and_outflows].expressions[:units_on][row.index], + cons_with.expressions[:units_on][row.index], base_name = "max_output_flow_with_basic_unit_commitment[$(row.asset),$(row.year),$(row.rep_period),$(row.time_block_start):$(row.time_block_end)]" - ) for - row in eachrow(constraints[:units_on_and_outflows].indices) if row.asset ∈ Auc_basic + ) for row in eachrow(cons_with.indices) if row.asset ∈ Auc_basic ], ) ## Ramping Constraints with unit commitment # Note: We start ramping constraints from the second timesteps_block # We filter and group the dataframe per asset and representative period - df_grouped = DataFrames.groupby( - constraints[:units_on_and_outflows].indices, - [:asset, :year, :rep_period], - ) + df_grouped = DataFrames.groupby(cons_with.indices, [:asset, :year, :rep_period]) # get the units on column to get easier the index - 1, i.e., the previous one - units_on = constraints[:units_on_and_outflows].expressions[:units_on] + units_on = cons_with.expressions[:units_on] #- Maximum ramp-up rate limit to the flow above the operating point when having unit commitment variables for ((a, y, rp), sub_df) in pairs(df_grouped) @@ -144,7 +141,7 @@ function add_ramping_constraints!(model, variables, constraints, graph, sets) ## Ramping Constraints without unit commitment # Note: We start ramping constraints from the second timesteps_block # We filter and group the dataframe per asset and representative period that does not have the unit_commitment methods - df_grouped = DataFrames.groupby(constraints[:highest_out].indices, [:asset, :year, :rep_period]) + df_grouped = DataFrames.groupby(cons_without.indices, [:asset, :year, :rep_period]) # get the expression from the capacity constraints for the highest_out assets_profile_times_capacity_out = model[:assets_profile_times_capacity_out] @@ -157,14 +154,13 @@ function add_ramping_constraints!(model, variables, constraints, graph, sets) model[Symbol("max_ramp_up_without_unit_commitment_$(a)_$(y)_$(rp)")] = [ @constraint( model, - outgoing_flow_highest_out_resolution[row.index] - - outgoing_flow_highest_out_resolution[row.index-1] ≤ + outgoing_flow[row.index] - outgoing_flow[row.index-1] ≤ graph[row.asset].max_ramp_up * row.min_outgoing_flow_duration * assets_profile_times_capacity_out[row.index], base_name = "max_ramp_up_without_unit_commitment[$a,$y,$rp,$(row.time_block_start):$(row.time_block_end)]" - ) for (k, row) in enumerate(eachrow(sub_df)) if - k > 1 && outgoing_flow_highest_out_resolution[row.index] != 0 + ) for + (k, row) in enumerate(eachrow(sub_df)) if k > 1 && outgoing_flow[row.index] != 0 ] end @@ -176,14 +172,13 @@ function add_ramping_constraints!(model, variables, constraints, graph, sets) model[Symbol("max_ramp_down_without_unit_commitment_$(a)_$(y)_$(rp)")] = [ @constraint( model, - outgoing_flow_highest_out_resolution[row.index] - - outgoing_flow_highest_out_resolution[row.index-1] ≥ + outgoing_flow[row.index] - outgoing_flow[row.index-1] ≥ -graph[row.asset].max_ramp_down * row.min_outgoing_flow_duration * assets_profile_times_capacity_out[row.index], base_name = "max_ramp_down_without_unit_commitment[$a,$y,$rp,$(row.time_block_start):$(row.time_block_end)]" - ) for (k, row) in enumerate(eachrow(sub_df)) if - k > 1 && outgoing_flow_highest_out_resolution[row.index] != 0 + ) for + (k, row) in enumerate(eachrow(sub_df)) if k > 1 && outgoing_flow[row.index] != 0 ] end end diff --git a/src/constraints/storage.jl b/src/constraints/storage.jl index c1da1cc9..b3ceaa86 100644 --- a/src/constraints/storage.jl +++ b/src/constraints/storage.jl @@ -19,13 +19,13 @@ function add_storage_constraints!(model, variables, constraints, graph) accumulated_energy_capacity = model[:accumulated_energy_capacity] incoming_flow_lowest_storage_resolution_intra_rp = - constraints[:storage_level_intra_rp].expressions[:incoming] + constraints[:balance_storage_rep_period].expressions[:incoming] outgoing_flow_lowest_storage_resolution_intra_rp = - constraints[:storage_level_intra_rp].expressions[:outgoing] + constraints[:balance_storage_rep_period].expressions[:outgoing] incoming_flow_storage_inter_rp_balance = - constraints[:storage_level_inter_rp].expressions[:incoming] + constraints[:balance_storage_over_clustered_year].expressions[:incoming] outgoing_flow_storage_inter_rp_balance = - constraints[:storage_level_inter_rp].expressions[:outgoing] + constraints[:balance_storage_over_clustered_year].expressions[:outgoing] # - Balance constraint (using the lowest temporal resolution) for ((a, y, rp), sub_df) in pairs(df_storage_intra_rp_balance_grouped) @@ -68,7 +68,7 @@ function add_storage_constraints!(model, variables, constraints, graph) # - Maximum storage level attach_constraint!( model, - constraints[:storage_level_intra_rp], + constraints[:balance_storage_rep_period], :max_storage_level_intra_rp_limit, [ @constraint( @@ -91,7 +91,7 @@ function add_storage_constraints!(model, variables, constraints, graph) # - Minimum storage level attach_constraint!( model, - constraints[:storage_level_intra_rp], + constraints[:balance_storage_rep_period], :min_storage_level_intra_rp_limit, [ @constraint( @@ -146,7 +146,7 @@ function add_storage_constraints!(model, variables, constraints, graph) ) end ) + - constraints[:storage_level_inter_rp].expressions[:inflows_profile_aggregation][row.index] + + constraints[:balance_storage_over_clustered_year].expressions[:inflows_profile_aggregation][row.index] + incoming_flow_storage_inter_rp_balance[row.index] - outgoing_flow_storage_inter_rp_balance[row.index], base_name = "storage_inter_rp_balance[$a,$(row.year),$(row.period_block_start):$(row.period_block_end)]" @@ -157,7 +157,7 @@ function add_storage_constraints!(model, variables, constraints, graph) # - Maximum storage level attach_constraint!( model, - constraints[:storage_level_inter_rp], + constraints[:balance_storage_over_clustered_year], :max_storage_level_inter_rp_limit, [ @constraint( @@ -180,7 +180,7 @@ function add_storage_constraints!(model, variables, constraints, graph) # - Minimum storage level attach_constraint!( model, - constraints[:storage_level_inter_rp], + constraints[:balance_storage_over_clustered_year], :min_storage_level_inter_rp_limit, [ @constraint( diff --git a/src/create-model.jl b/src/create-model.jl index 06e1506f..eb19b574 100644 --- a/src/create-model.jl +++ b/src/create-model.jl @@ -122,12 +122,7 @@ function create_model( @timeit to "add_energy_constraints!" add_energy_constraints!(model, constraints, graph) - @timeit to "add_consumer_constraints!" add_consumer_constraints!( - model, - constraints, - graph, - sets, - ) + @timeit to "add_consumer_constraints!" add_consumer_constraints!(model, constraints, graph) @timeit to "add_storage_constraints!" add_storage_constraints!( model, @@ -136,9 +131,9 @@ function create_model( graph, ) - @timeit to "add_hub_constraints!" add_hub_constraints!(model, constraints, sets) + @timeit to "add_hub_constraints!" add_hub_constraints!(model, constraints) - @timeit to "add_conversion_constraints!" add_conversion_constraints!(model, constraints, sets) + @timeit to "add_conversion_constraints!" add_conversion_constraints!(model, constraints) @timeit to "add_transport_constraints!" add_transport_constraints!( model, @@ -157,7 +152,7 @@ function create_model( ) end - if !isempty(constraints[:units_on_and_outflows].indices) + if !isempty(constraints[:ramping_with_unit_commitment].indices) @timeit to "add_ramping_constraints!" add_ramping_constraints!( model, variables, diff --git a/src/io.jl b/src/io.jl index 7114c97d..83ae7b41 100644 --- a/src/io.jl +++ b/src/io.jl @@ -468,14 +468,14 @@ function save_solution_to_file(output_folder, graph, solution) # # output_file = joinpath(output_folder, "max-energy-inter-rp.csv") # output_table = - # DataFrames.select(dataframes[:max_energy_inter_rp], :asset, :periods_block => :period) - # output_table.value = solution.max_energy_inter_rp + # DataFrames.select(dataframes[:max_energy_over_clustered_year], :asset, :periods_block => :period) + # output_table.value = solution.max_energy_over_clustered_year # output_table |> CSV.write(output_file) # # output_file = joinpath(output_folder, "min-energy-inter-rp.csv") # output_table = - # DataFrames.select(dataframes[:min_energy_inter_rp], :asset, :periods_block => :period) - # output_table.value = solution.min_energy_inter_rp + # DataFrames.select(dataframes[:min_energy_over_clustered_year], :asset, :periods_block => :period) + # output_table.value = solution.min_energy_over_clustered_year # output_table |> CSV.write(output_file) return diff --git a/src/model-preparation.jl b/src/model-preparation.jl index 253ad948..253b370a 100644 --- a/src/model-preparation.jl +++ b/src/model-preparation.jl @@ -331,7 +331,7 @@ function add_expressions_to_constraints!( # Unpack variables # Creating the incoming and outgoing flow expressions @timeit to "add_expression_terms_intra_rp_constraints!" add_expression_terms_intra_rp_constraints!( - constraints[:lowest], + constraints[:balance_conversion], variables[:flow], expression_workspace, representative_periods, @@ -340,7 +340,7 @@ function add_expressions_to_constraints!( multiply_by_duration = true, ) @timeit to "add_expression_terms_intra_rp_constraints!" add_expression_terms_intra_rp_constraints!( - constraints[:storage_level_intra_rp], + constraints[:balance_storage_rep_period], variables[:flow], expression_workspace, representative_periods, @@ -349,7 +349,7 @@ function add_expressions_to_constraints!( multiply_by_duration = true, ) @timeit to "add_expression_terms_intra_rp_constraints!" add_expression_terms_intra_rp_constraints!( - constraints[:highest_in_out], + constraints[:balance_consumer], variables[:flow], expression_workspace, representative_periods, @@ -358,7 +358,7 @@ function add_expressions_to_constraints!( multiply_by_duration = false, ) @timeit to "add_expression_terms_intra_rp_constraints!" add_expression_terms_intra_rp_constraints!( - constraints[:highest_in], + constraints[:balance_hub], variables[:flow], expression_workspace, representative_periods, @@ -367,7 +367,26 @@ function add_expressions_to_constraints!( multiply_by_duration = false, ) @timeit to "add_expression_terms_intra_rp_constraints!" add_expression_terms_intra_rp_constraints!( - constraints[:highest_out], + constraints[:capacity_incoming], + variables[:flow], + expression_workspace, + representative_periods, + graph; + use_highest_resolution = true, + multiply_by_duration = false, + ) + @timeit to "add_expression_terms_intra_rp_constraints!" add_expression_terms_intra_rp_constraints!( + constraints[:capacity_outgoing], + variables[:flow], + expression_workspace, + representative_periods, + graph; + use_highest_resolution = true, + multiply_by_duration = false, + add_min_outgoing_flow_duration = true, + ) + @timeit to "add_expression_terms_intra_rp_constraints!" add_expression_terms_intra_rp_constraints!( + constraints[:ramping_without_unit_commitment], variables[:flow], expression_workspace, representative_periods, @@ -376,9 +395,9 @@ function add_expressions_to_constraints!( multiply_by_duration = false, add_min_outgoing_flow_duration = true, ) - if !isempty(constraints[:units_on_and_outflows].indices) + if !isempty(constraints[:ramping_with_unit_commitment].indices) @timeit to "add_expression_terms_intra_rp_constraints!" add_expression_terms_intra_rp_constraints!( - constraints[:units_on_and_outflows], + constraints[:ramping_with_unit_commitment], variables[:flow], expression_workspace, representative_periods, @@ -389,7 +408,7 @@ function add_expressions_to_constraints!( ) end @timeit to "add_expression_terms_inter_rp_constraints!" add_expression_terms_inter_rp_constraints!( - constraints[:storage_level_inter_rp], + constraints[:balance_storage_over_clustered_year], variables[:flow], timeframe.map_periods_to_rp, graph, @@ -397,32 +416,39 @@ function add_expressions_to_constraints!( is_storage_level = true, ) @timeit to "add_expression_terms_inter_rp_constraints!" add_expression_terms_inter_rp_constraints!( - constraints[:max_energy_inter_rp], + constraints[:max_energy_over_clustered_year], variables[:flow], timeframe.map_periods_to_rp, graph, representative_periods, ) @timeit to "add_expression_terms_inter_rp_constraints!" add_expression_terms_inter_rp_constraints!( - constraints[:min_energy_inter_rp], + constraints[:min_energy_over_clustered_year], variables[:flow], timeframe.map_periods_to_rp, graph, representative_periods, ) @timeit to "add_expression_is_charging_terms_intra_rp_constraints!" add_expression_is_charging_terms_intra_rp_constraints!( - constraints[:highest_in], + constraints[:capacity_incoming], variables[:is_charging], expression_workspace, ) @timeit to "add_expression_is_charging_terms_intra_rp_constraints!" add_expression_is_charging_terms_intra_rp_constraints!( - constraints[:highest_out], + constraints[:capacity_outgoing], variables[:is_charging], expression_workspace, ) - if !isempty(constraints[:units_on_and_outflows].indices) + if !isempty(constraints[:ramping_with_unit_commitment].indices) + @timeit to "add_expression_units_on_terms_intra_rp_constraints!" add_expression_units_on_terms_intra_rp_constraints!( + constraints[:ramping_with_unit_commitment], + variables[:units_on], + expression_workspace, + ) + end + if !isempty(constraints[:ramping_without_unit_commitment].indices) @timeit to "add_expression_units_on_terms_intra_rp_constraints!" add_expression_units_on_terms_intra_rp_constraints!( - constraints[:units_on_and_outflows], + constraints[:ramping_without_unit_commitment], variables[:units_on], expression_workspace, ) diff --git a/src/solve-model.jl b/src/solve-model.jl index f5abcab6..b8bcf110 100644 --- a/src/solve-model.jl +++ b/src/solve-model.jl @@ -48,14 +48,14 @@ function solve_model!( # graph[a].storage_level_inter_rp[pb] = value # end # - # for row in eachrow(energy_problem.dataframes[:max_energy_inter_rp]) + # for row in eachrow(energy_problem.dataframes[:max_energy_over_clustered_year]) # a, pb, value = row.asset, row.periods_block, row.solution - # graph[a].max_energy_inter_rp[pb] = value + # graph[a].max_energy_over_clustered_year[pb] = value # end # - # for row in eachrow(energy_problem.dataframes[:min_energy_inter_rp]) + # for row in eachrow(energy_problem.dataframes[:max_energy_over_clustered_year]) # a, pb, value = row.asset, row.periods_block, row.solution - # graph[a].min_energy_inter_rp[pb] = value + # graph[a].max_energy_over_clustered_year[pb] = value # end for ((y, (u, v)), value) in energy_problem.solution.flows_investment @@ -95,8 +95,8 @@ function solve_model!(model, args...; kwargs...) # dataframes[:flow].solution = solution.flow # dataframes[:storage_level_intra_rp].solution = solution.storage_level_intra_rp # dataframes[:storage_level_inter_rp].solution = solution.storage_level_inter_rp - # dataframes[:max_energy_inter_rp].solution = solution.max_energy_inter_rp - # dataframes[:min_energy_inter_rp].solution = solution.min_energy_inter_rp + # dataframes[:max_energy_over_clustered_year].solution = solution.max_energy_over_clustered_year + # dataframes[:min_energy_over_clustered_year].solution = solution.min_energy_over_clustered_year return solution end @@ -191,8 +191,8 @@ function solve_model( Dict(k => JuMP.value(v) for (k, v) in variables[:flows_investment].lookup), JuMP.value.(variables[:storage_level_intra_rp].container), JuMP.value.(variables[:storage_level_inter_rp].container), - JuMP.value.(model[:max_energy_inter_rp]), - JuMP.value.(model[:min_energy_inter_rp]), + JuMP.value.(model[:max_energy_over_clustered_year]), + JuMP.value.(model[:min_energy_over_clustered_year]), JuMP.value.(variables[:flow].container), JuMP.objective_value(model), dual_variables, diff --git a/src/structures.jl b/src/structures.jl index a2d57255..96a010a2 100644 --- a/src/structures.jl +++ b/src/structures.jl @@ -212,8 +212,8 @@ mutable struct GraphAssetData investment_energy::Dict{Int,Float64} # for storage assets with energy method storage_level_intra_rp::Dict{Tuple{Int,TimestepsBlock},Float64} storage_level_inter_rp::Dict{PeriodsBlock,Float64} - max_energy_inter_rp::Dict{PeriodsBlock,Float64} - min_energy_inter_rp::Dict{PeriodsBlock,Float64} + max_energy_over_clustered_year::Dict{PeriodsBlock,Float64} + min_energy_over_clustered_year::Dict{PeriodsBlock,Float64} # You don't need profiles to create the struct, so initiate it empty function GraphAssetData(args...) @@ -302,8 +302,8 @@ mutable struct Solution flows_investment::Any # TODO: Fix this type storage_level_intra_rp::Vector{Float64} storage_level_inter_rp::Vector{Float64} - max_energy_inter_rp::Vector{Float64} - min_energy_inter_rp::Vector{Float64} + max_energy_over_clustered_year::Vector{Float64} + min_energy_over_clustered_year::Vector{Float64} flow::Vector{Float64} objective_value::Float64 duals::Union{Nothing,Dict{Symbol,Vector{Float64}}} diff --git a/src/tmp.jl b/src/tmp.jl index 787e93f8..bab41203 100644 --- a/src/tmp.jl +++ b/src/tmp.jl @@ -459,6 +459,7 @@ function tmp_example_of_flow_expression_problem() ref: see $(@__FILE__):$(@__LINE__) + # TODO: This is outdated The desired incoming and outgoing flows are: - ep.dataframes[:highest_in_out].incoming_flow - ep.dataframes[:highest_in_out].outgoing_flow